On 5/27/2022 5:09 PM, Glen Choo via GitGitGadget wrote: > From: Glen Choo <chooglen@xxxxxxxxxx> > > Protected config is read using `read_very_early_config()`, which has > several downsides: > > - Every call to `read_very_early_config()` parses global and > system-level config files anew, but this can be optimized by just > parsing them once [1]. > - Protected variables should respect "-c" because we can reasonably > assume that it comes from the user. But, `read_very_early_config()` > can't use "-c" because it is called so early that it does not have > access to command line arguments. > > Introduce `git_protected_config()`, which reads protected config and > caches the values in `the_repository.protected_config`. Then, refactor > `safe.directory` to use `git_protected_config()`. > > This implementation can still be improved, however: > > - `git_protected_config()` iterates through every variable in > `the_repository.protected_config`, which may still be too expensive to > be called in every "git" invocation. There exist constant time lookup > functions for non-protected config (repo_config_get_*()), but for > simplicity, this commit does not implement similar functions for > protected config. I originally thought that we should jump to that "right" solution, but the existing logic in ensure_valid_ownership() uses the iterator method, mostly because it uses a multi-valued string. There are helpers that allow iterating over a specific multi-valued key, but there is no reason to complicate the current patch with that amount of refactoring. That can be handled as a completely separate topic. > - Protected config is stored in `the_repository` so that we don't need > to statically allocate it. But this might be confusing since protected > config ignores repository config by definition. I agree with Junio's suggestion of keeping this as a static global in config.c, accessible only by the public methods from config.h. A future where we have "the_world" might be nice for inventory on all these globals. Definitely not something to hold up this series. > +/* Read protected config into the_repository->protected_config. */ > +static void read_protected_config(void) > +{ > + char *xdg_config = NULL, *user_config = NULL, *system_config = NULL; > + > + CALLOC_ARRAY(the_repository->protected_config, 1); > + git_configset_init(the_repository->protected_config); > + > + system_config = git_system_config(); > + git_global_config(&user_config, &xdg_config); > + > + git_configset_add_file(the_repository->protected_config, system_config); > + git_configset_add_file(the_repository->protected_config, xdg_config); > + git_configset_add_file(the_repository->protected_config, user_config); > + > + free(system_config); > + free(xdg_config); > + free(user_config); > +} This loads the config from three files, including the xdg_config, which I wasn't thinking about before. This implementation does not use the -c config yet, which you listed as a downside of read_very_early_config(). I see that you include that in your patch 4, but the commit message for this patch could list that as a step that will be handled by a later change. (You could also do that as patch 3 and add a test near the existing safe.directory tests instead of waiting for discovery.bare.) > + > +/* Ensure that the_repository->protected_config has been initialized. */ > +static void git_protected_config_check_init(void) > +{ > + if (the_repository->protected_config && > + the_repository->protected_config->hash_initialized) > + return; > + read_protected_config(); > +} > + > +void git_protected_config(config_fn_t fn, void *data) > +{ > + git_protected_config_check_init(); > + configset_iter(the_repository->protected_config, fn, data); > +} These two methods are clearly correct. ..._check_init() is an OK name. I've seen us use "prepare_...()" in other areas as a way of making sure that we have the proper state (see prepare_packed_git() and the like), so maybe a rename here to match would be worthwhile. Feel free to ignore. > + if (repo->protected_config) { > + git_configset_clear(repo->protected_config); > + FREE_AND_NULL(repo->protected_config); > + } This will have no equivalent when protected_config is left as a static global, but that is fine. It only goes out of scope with the end of the process, anyway. > @@ -1128,7 +1128,7 @@ static int ensure_valid_ownership(const char *path) > is_path_owned_by_current_user(path)) > return 1; > > - read_very_early_config(safe_directory_cb, &data); > + git_protected_config(safe_directory_cb, &data); Nice to have a very simple conversion here. Thanks, -Stolee