| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
| |
When a config file contains `[includeIf]` (with no condition), we should
treat that as a falsey value. This means that we should properly parse
a config value of `includeIf.path`.
|
| |
|
|
|
|
| |
(fixes issue #6088)
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| |
|
|
| |
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| |
|
|
|
|
| |
(fixes issue #6089)
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| | |
|
| |
|
|
|
|
| |
Introduce `git_fs_path`, which operates on generic filesystem paths.
`git_path` will be kept for only git-specific path functionality (for
example, checking for `.git` in a path).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
libgit2 has two distinct requirements that were previously solved by
`git_buf`. We require:
1. A general purpose string class that provides a number of utility APIs
for manipulating data (eg, concatenating, truncating, etc).
2. A structure that we can use to return strings to callers that they
can take ownership of.
By using a single class (`git_buf`) for both of these purposes, we have
confused the API to the point that refactorings are difficult and
reasoning about correctness is also difficult.
Move the utility class `git_buf` to be called `git_str`: this represents
its general purpose, as an internal string buffer class. The name also
is an homage to Junio Hamano ("gitstr").
The public API remains `git_buf`, and has a much smaller footprint. It
is generally only used as an "out" param with strict requirements that
follow the documentation. (Exceptions exist for some legacy APIs to
avoid breaking callers unnecessarily.)
Utility functions exist to convert a user-specified `git_buf` to a
`git_str` so that we can call internal functions, then converting it
back again.
|
| |
|
|
|
| |
This tests parsing a multiline string containing multiple quoted comment
chars. See #6019
|
| | |
|
| |
|
|
|
|
|
|
|
|
| |
If an included config is refreshed twice, the second update is not taken
into account.
This is because the list of included files is cleared after re-reading
the new configuration, instead of being cleared before.
Fix it and add a test case to check for this bug.
|
| |\
| |
| | |
boolean config parsing fails in some cases with mapped values
|
| | |
| |
| |
| | |
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| | |
| |
| |
| | |
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| | |
| |
| |
| | |
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| | |
| |
| |
| |
| |
| | |
git_config_lookup_map_value
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| |/
|
|
| |
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| | |
|
| |
|
|
|
|
| |
We should always verify error codes returned by function calls in our
test suite to not accidentally miss any weird results. Coverity reported
missing checks in several locations, which this commit fixes.
|
| |
|
|
|
|
|
|
| |
The test in config::stress::huge_section_with_many_values takes quite a
long time to execute. Hide it behind the GITTEST_INVASIVE_SPEED
environment varibale to not needlessly blow up execution time of tests.
As this environment variable is being set by the continuous integration,
we will execute it regularly anyway.
|
| |\
| |
| | |
Memory optimizations for config entries
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Multivars are configuration entries that have many values for the same
name; we can thus micro-optimize this case by just retaining the name of
the first configuration entry and freeing all the others, letting them
point to the string of the first entry.
The attached test case is an extreme example that demonstrates this. It
contains a section name that is approximately 500kB in size with 20.000
entries "a=b". Without the optimization, this would require at least
20000*500kB bytes, which is around 10GB. With this patch, it only
requires 500kB+20000*1B=20500kB.
The obvious culprit here is the section header, which we repeatedly
include in each of the configuration entry's names. This makes it very
easier for an adversary to provide a small configuration file that
disproportionally blows up in memory during processing and is thus a
feasible way for a denial-of-service attack. Unfortunately, we cannot
fix the root cause by e.g. having a separate "section" field that may
easily be deduplicated due to the `git_config_entry` structure being
part of our public API. So this micro-optimization is the best we can do
for now.
|
| |/
|
|
|
|
|
|
| |
Similar as in commit dadbb33b6 (Fix crash if snapshotting a
config_snapshot, 2019-11-01), let's implement snapshots for in-memory
configuration entries. As this deletes more code than it adds, it
doesn't make any sense to not allow for this and allows users to treat
config backends mostly the same.
|
| |
|
|
| |
Signed-off-by: Sven Strickroth <email@cs-ware.de>
|
| |
|
|
|
|
|
|
|
| |
With Git v2.23.0, the conditional include mechanism gained another new
conditional "onbranch". As the name says, it will cause a file to be
included if the "onbranch" pattern matches the currently checked out
branch.
Implement this new condition and add a bunch of tests.
|
| |
|
|
|
|
| |
When assembling contents of the conditionally including file, we use
`git_buf_printf` and `git_buf_puts` without checking for error returns.
Add `cl_git_pass` to fix this.
|
| |
|
|
|
|
|
| |
Add a few tests that verify some behaviour centered around includes. The
first set of tests verifies that we correctly override values depending
on the order of includes and other keys, the second set asserts that we
can correctly snapshot configuration files with includes.
|
| |
|
|
|
|
| |
Modernize the tests in config::snapshot to make them easier to
understand. Most important, include a cleanup function that frees config
and snapshot and unlink config files at the end of each test.
|
| |
|
|
|
|
|
|
|
|
| |
When creating a new iterator for a config file backend, then we should
always make sure that we're up to date by calling `config_refresh`.
Otherwise, we might not notice when another process has modified the
configuration file and thus will represent outdated values.
Add two tests to config::stress that verify that we get up-to-date
values when reading configuration entries via `git_config_iterator`.
|
| |
|
|
|
|
|
|
|
| |
Our file utils functions all have a "futils" prefix, e.g.
`git_futils_touch`. One would thus naturally guess that their
definitions and implementation would live in files "futils.h" and
"futils.c", respectively, but in fact they live in "fileops.h".
Rename the files to match expectations.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
To decide whether a config file has changed, we always hash its
complete contents. This is unnecessarily expensive, as
well-behaved filesystems will always update stat information for
files which have changed. So before computing the hash, we should
first check whether the stat info has actually changed for either
the configuration file or any of its includes. This avoids having
to re-read the configuration file and its includes every time
when we check whether it's been modified.
Tracing the for-each-ref example previous to this commit, one can
see that we repeatedly re-open both the repo configuration as
well as the global configuration:
$ strace lg2 for-each-ref |& grep config
access("/home/pks/.gitconfig", F_OK) = -1 ENOENT (No such file or directory)
access("/home/pks/.config/git/config", F_OK) = 0
access("/etc/gitconfig", F_OK) = -1 ENOENT (No such file or directory)
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
access("/tmp/repo/.git/config", F_OK) = 0
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/home/pks/.gitconfig", 0x7ffd15c05290) = -1 ENOENT (No such file or directory)
access("/home/pks/.gitconfig", F_OK) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
access("/home/pks/.config/git/config", F_OK) = 0
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/home/pks/.gitconfig", 0x7ffd15c051f0) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/home/pks/.gitconfig", 0x7ffd15c05090) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/home/pks/.gitconfig", 0x7ffd15c05090) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/home/pks/.gitconfig", 0x7ffd15c05090) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
With the change, we only do stats for those files and open them a
single time, only:
$ strace lg2 for-each-ref |& grep config
access("/home/pks/.gitconfig", F_OK) = -1 ENOENT (No such file or directory)
access("/home/pks/.config/git/config", F_OK) = 0
access("/etc/gitconfig", F_OK) = -1 ENOENT (No such file or directory)
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
access("/tmp/repo/.git/config", F_OK) = 0
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
open("/tmp/repo/.git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/home/pks/.gitconfig", 0x7ffe70540d20) = -1 ENOENT (No such file or directory)
access("/home/pks/.gitconfig", F_OK) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
access("/home/pks/.config/git/config", F_OK) = 0
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
open("/home/pks/.config/git/config", O_RDONLY|O_CLOEXEC) = 3
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
stat("/home/pks/.gitconfig", 0x7ffe70540ca0) = -1 ENOENT (No such file or directory)
stat("/home/pks/.gitconfig", 0x7ffe70540c80) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
stat("/home/pks/.gitconfig", 0x7ffe70540b40) = -1 ENOENT (No such file or directory)
stat("/home/pks/.gitconfig", 0x7ffe70540b20) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
stat("/home/pks/.gitconfig", 0x7ffe70540b40) = -1 ENOENT (No such file or directory)
stat("/home/pks/.gitconfig", 0x7ffe70540b20) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
stat("/tmp/repo/.git/config", {st_mode=S_IFREG|0644, st_size=92, ...}) = 0
stat("/home/pks/.gitconfig", 0x7ffe70540b40) = -1 ENOENT (No such file or directory)
stat("/home/pks/.gitconfig", 0x7ffe70540b20) = -1 ENOENT (No such file or directory)
stat("/home/pks/.config/git/config", {st_mode=S_IFREG|0644, st_size=1154, ...}) = 0
The following benchmark has been performed with and without the
stat cache in a best-of-ten run:
```
int lg2_repro(git_repository *repo, int argc, char **argv)
{
git_config *cfg;
int32_t dummy;
int i;
UNUSED(argc);
UNUSED(argv);
check_lg2(git_repository_config(&cfg, repo),
"Could not obtain config", NULL);
for (i = 1; i < 100000; ++i)
git_config_get_int32(&dummy, cfg, "foo.bar");
git_config_free(cfg);
return 0;
}
```
Without stat cache:
$ time lg2 repro
real 0m1.528s
user 0m0.568s
sys 0m0.944s
With stat cache:
$ time lg2 repro
real 0m0.526s
user 0m0.268s
sys 0m0.258s
This benchmark shows a nearly three-fold performance improvement.
This change requires that we check our configuration stress tests
as we're now in fact becoming more racy. If somebody is writing a
configuration file at nearly the same time (there is a window of
100ns on Windows-based systems), then it might be that we realize
that this file has actually changed and thus may not re-read it.
This will only happen if either an external process is rewriting
the configuration file or if the same process has multiple
`git_config` structures pointing to the same time, where one of
both is being used to write and the other one is used to read
values.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
We currently use `p_fnmatch` to compute whether a given "gitdir:"
or "gitdir/i:" conditional matches the current configuration file
path. As git.git has moved to use `wildmatch` instead of
`p_fnmatch` throughout its complete codebase, we evaluate
conditionals inconsistently with git.git in some special cases.
Convert `p_fnmatch` to use `wildmatch`. The `FNM_LEADINGDIR` flag
cannot be translated to `wildmatch`, but in fact git.git doesn't
use it here either. And in fact, dropping it while we go
increases compatibility with git.git.
|
| |
|
|
|
|
|
|
|
|
|
| |
When evaluating "gitdir:" and "gitdir/i:" conditionals, we
currently compare the given pattern with the value of
`git_repository_path`. Thing is though that `git_repository_path`
returns the gitdir path with trailing '/', while we actually need
to match against the gitdir without it.
Fix this issue by stripping the trailing '/' previous to
matching. Add various tests to ensure we get this right.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
When we reach a whitespace after a section name, we assume that what
will follow will be a quoted subsection name. Pass the current position
of the line being parsed to the subsection parser, so that it can
validate that subsequent characters are additional whitespace or a
single quote.
Previously we would begin parsing after the section name, looking for
the first quotation mark. This allows invalid characters to embed
themselves between the end of the section name and the first quotation
mark, eg `[section foo "subsection"]`, which is illegal.
|
| |
|
|
|
|
|
| |
Opening a default config when ~/.gitconfig doesn't exist, locking it,
and attempting to write to it causes an assertion failure.
Treat non-existent global config file content as an empty string.
|
| |
|
|
|
|
| |
According to reports, libgit2 is unable to read a global configuration
file that is simply a symlink to the real configuration. Write a
(succeeding) test that shows that libgit2 _is_ correctly able to do so.
|
| |
|
|
|
|
|
| |
While we do verify that we are able to open the global ".gitconfig" file
in config::global::open_global, we never verify that we it is in fact
readable. Do so by writing the global configuration file and verifying
that reading from it produces the expected values.
|
| |
|
|
|
|
|
|
|
| |
The config::global test suite creates various different directories and
files which are being populated with pretend-global files.
Unfortunately, the tests do not clean up after themselves, which may
cause subsequent tests to fail due to cruft left behind.
Fix this by always removing created directories and their contents.
|
| |
|
|
|
| |
Move to the `git_error` name in the internal API for error-related
functions.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
When we try to add a configuration file with `git_config_add_file_ondisk`, we
treat nonexisting files as empty. We do this by performing a stat call, ignoring
ENOENT errors. This works just fine in case the file or any of its parents
simply does not exist, but there is also the case where any of the parent
directories is not a directory, but a file. So e.g. trying to add a
configuration file "/dev/null/.gitconfig" will fail, as `errno` will be ENOTDIR
instead of ENOENT.
Catch ENOTDIR in addition to ENOENT to fix the issue. Add a test that verifies
we are able to add configuration files with such an invalid path file just fine.
|
| | |
|
| |\
| |
| | |
config: Port config_file_fuzzer to the new in-memory backend.
|
| | | |
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
In case a configuration includes a key "include.path=" without any
value, the generated configuration entry will have its value set to
`NULL`. This is unexpected by the logic handling includes, and as soon
as we try to calculate the included path we will unconditionally
dereference that `NULL` pointer and thus segfault.
Fix the issue by returning early in both `parse_include` and
`parse_conditional_include` in case where the `file` argument is `NULL`.
Add a test to avoid future regression.
The issue has been found by the oss-fuzz project, issue 10810.
|
| |/
|
|
|
|
|
|
|
|
| |
While our tests in config::include create a plethora of configuration
files, most of them do not get removed at the end of each test. This can
cause weird interactions with tests that are being run at a later stage
if these later tests try to create files or directories with the same
name as any of the created configuration files.
Fix the issue by unlinking all created files at the end of these tests.
|
| |
|
|
|
|
|
|
|
| |
Now that we have abstracted away how to store and retrieve config
entries, it became trivial to implement a new in-memory backend by
making use of this. And thus we do so.
This commit implements a new read-only in-memory backend that can parse
a chunk of memory into a `git_config_backend` structure.
|
| |
|
|
|
|
|
|
|
|
| |
The header "config_file.h" has a list of inline-functions to access the
contents of a config backend without directly messing with the struct's
function pointers. While all these functions are called
"git_config_file_*", they are in fact completely backend-agnostic and
don't care whether it is a file or not. Rename all the function to
instead be backend-agnostic versions called "git_config_backend_*" and
rename the header to match.
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
| |
C++ style comment ("//") are not specified by the ISO C90 standard and
thus do not conform to it. While libgit2 aims to conform to C90, we did
not enforce it until now, which is why quite a lot of these
non-conforming comments have snuck into our codebase. Do a tree-wide
conversion of all C++ style comments to the supported C style comments
to allow us enforcing strict C90 compliance in a later commit.
|
| | |
|