Merge branch 'ns/tmp-objdir'

New interface into the tmp-objdir API to help in-core use of the
quarantine feature.

* ns/tmp-objdir:
  tmp-objdir: disable ref updates when replacing the primary odb
  tmp-objdir: new API for creating temporary writable databases
This commit is contained in:
Junio C Hamano 2022-01-03 16:24:14 -08:00
commit 0dc90d954d
11 changed files with 183 additions and 15 deletions

View File

@ -26,10 +26,22 @@ static int prune_tmp_file(const char *fullpath)
return error("Could not stat '%s'", fullpath);
if (st.st_mtime > expire)
return 0;
if (show_only || verbose)
printf("Removing stale temporary file %s\n", fullpath);
if (!show_only)
unlink_or_warn(fullpath);
if (S_ISDIR(st.st_mode)) {
if (show_only || verbose)
printf("Removing stale temporary directory %s\n", fullpath);
if (!show_only) {
struct strbuf remove_dir_buf = STRBUF_INIT;
strbuf_addstr(&remove_dir_buf, fullpath);
remove_dir_recursively(&remove_dir_buf, 0);
strbuf_release(&remove_dir_buf);
}
} else {
if (show_only || verbose)
printf("Removing stale temporary file %s\n", fullpath);
if (!show_only)
unlink_or_warn(fullpath);
}
return 0;
}

View File

@ -2206,7 +2206,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
strvec_push(&child.args, alt_shallow_file);
}
tmp_objdir = tmp_objdir_create();
tmp_objdir = tmp_objdir_create("incoming");
if (!tmp_objdir) {
if (err_fd > 0)
close(err_fd);

View File

@ -17,6 +17,7 @@
#include "commit.h"
#include "strvec.h"
#include "object-store.h"
#include "tmp-objdir.h"
#include "chdir-notify.h"
#include "shallow.h"
@ -169,6 +170,10 @@ void setup_git_env(const char *git_dir)
args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
args.disable_ref_updates = 1;
}
repo_set_gitdir(the_repository, git_dir, &args);
strvec_clear(&to_free);
@ -332,10 +337,14 @@ static void update_relative_gitdir(const char *name,
void *data)
{
char *path = reparent_relative_path(old_cwd, new_cwd, get_git_dir());
struct tmp_objdir *tmp_objdir = tmp_objdir_unapply_primary_odb();
trace_printf_key(&trace_setup_key,
"setup: move $GIT_DIR to '%s'",
path);
set_git_dir_1(path);
if (tmp_objdir)
tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
free(path);
}

View File

@ -680,6 +680,49 @@ void add_to_alternates_memory(const char *reference)
'\n', NULL, 0);
}
struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)
{
struct object_directory *new_odb;
/*
* Make sure alternates are initialized, or else our entry may be
* overwritten when they are.
*/
prepare_alt_odb(the_repository);
/*
* Make a new primary odb and link the old primary ODB in as an
* alternate
*/
new_odb = xcalloc(1, sizeof(*new_odb));
new_odb->path = xstrdup(dir);
/*
* Disable ref updates while a temporary odb is active, since
* the objects in the database may roll back.
*/
new_odb->disable_ref_updates = 1;
new_odb->will_destroy = will_destroy;
new_odb->next = the_repository->objects->odb;
the_repository->objects->odb = new_odb;
return new_odb->next;
}
void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)
{
struct object_directory *cur_odb = the_repository->objects->odb;
if (strcmp(old_path, cur_odb->path))
BUG("expected %s as primary object store; found %s",
old_path, cur_odb->path);
if (cur_odb->next != restore_odb)
BUG("we expect the old primary object store to be the first alternate");
the_repository->objects->odb = restore_odb;
free_object_directory(cur_odb);
}
/*
* Compute the exact path an alternate is at and returns it. In case of
* error NULL is returned and the human readable error is added to `err`
@ -1806,8 +1849,11 @@ int hash_object_file(const struct git_hash_algo *algo, const void *buf,
/* Finalize a file on disk, and close it. */
static void close_loose_object(int fd)
{
if (fsync_object_files)
fsync_or_die(fd, "loose object file");
if (!the_repository->objects->odb->will_destroy) {
if (fsync_object_files)
fsync_or_die(fd, "loose object file");
}
if (close(fd) != 0)
die_errno(_("error when closing loose object file"));
}

View File

@ -27,6 +27,18 @@ struct object_directory {
uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
struct oidtree *loose_objects_cache;
/*
* This is a temporary object store created by the tmp_objdir
* facility. Disable ref updates since the objects in the store
* might be discarded on rollback.
*/
int disable_ref_updates;
/*
* This object store is ephemeral, so there is no need to fsync.
*/
int will_destroy;
/*
* Path to the alternative object store. If this is a relative path,
* it is relative to the current working directory.
@ -58,6 +70,17 @@ void add_to_alternates_file(const char *dir);
*/
void add_to_alternates_memory(const char *dir);
/*
* Replace the current writable object directory with the specified temporary
* object directory; returns the former primary object directory.
*/
struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
/*
* Restore a previous ODB replaced by set_temporary_main_odb.
*/
void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
/*
* Populate and return the loose object cache array corresponding to the
* given object ID.
@ -68,6 +91,9 @@ struct oidtree *odb_loose_cache(struct object_directory *odb,
/* Empty the loose object cache for the specified object directory. */
void odb_clear_loose_cache(struct object_directory *odb);
/* Clear and free the specified object directory */
void free_object_directory(struct object_directory *odb);
struct packed_git {
struct hashmap_entry packmap_ent;
struct packed_git *next;

View File

@ -513,7 +513,7 @@ struct raw_object_store *raw_object_store_new(void)
return o;
}
static void free_object_directory(struct object_directory *odb)
void free_object_directory(struct object_directory *odb)
{
free(odb->path);
odb_clear_loose_cache(odb);

2
refs.c
View File

@ -2145,7 +2145,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
break;
}
if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
if (refs->repo->objects->odb->disable_ref_updates) {
strbuf_addstr(err,
_("ref updates forbidden inside quarantine environment"));
return -1;

View File

@ -82,6 +82,8 @@ void repo_set_gitdir(struct repository *repo,
expand_base_dir(&repo->objects->odb->path, o->object_dir,
repo->commondir, "objects");
repo->objects->odb->disable_ref_updates = o->disable_ref_updates;
free(repo->objects->alternate_db);
repo->objects->alternate_db = xstrdup_or_null(o->alternate_db);
expand_base_dir(&repo->graft_file, o->graft_file,

View File

@ -162,6 +162,7 @@ struct set_gitdir_args {
const char *graft_file;
const char *index_file;
const char *alternate_db;
int disable_ref_updates;
};
void repo_set_gitdir(struct repository *repo, const char *root,

View File

@ -1,5 +1,6 @@
#include "cache.h"
#include "tmp-objdir.h"
#include "chdir-notify.h"
#include "dir.h"
#include "sigchain.h"
#include "string-list.h"
@ -11,6 +12,8 @@
struct tmp_objdir {
struct strbuf path;
struct strvec env;
struct object_directory *prev_odb;
int will_destroy;
};
/*
@ -38,6 +41,9 @@ static int tmp_objdir_destroy_1(struct tmp_objdir *t, int on_signal)
if (t == the_tmp_objdir)
the_tmp_objdir = NULL;
if (!on_signal && t->prev_odb)
restore_primary_odb(t->prev_odb, t->path.buf);
/*
* This may use malloc via strbuf_grow(), but we should
* have pre-grown t->path sufficiently so that this
@ -52,6 +58,7 @@ static int tmp_objdir_destroy_1(struct tmp_objdir *t, int on_signal)
*/
if (!on_signal)
tmp_objdir_free(t);
return err;
}
@ -121,7 +128,7 @@ static int setup_tmp_objdir(const char *root)
return ret;
}
struct tmp_objdir *tmp_objdir_create(void)
struct tmp_objdir *tmp_objdir_create(const char *prefix)
{
static int installed_handlers;
struct tmp_objdir *t;
@ -129,11 +136,16 @@ struct tmp_objdir *tmp_objdir_create(void)
if (the_tmp_objdir)
BUG("only one tmp_objdir can be used at a time");
t = xmalloc(sizeof(*t));
t = xcalloc(1, sizeof(*t));
strbuf_init(&t->path, 0);
strvec_init(&t->env);
strbuf_addf(&t->path, "%s/incoming-XXXXXX", get_object_directory());
/*
* Use a string starting with tmp_ so that the builtin/prune.c code
* can recognize any stale objdirs left behind by a crash and delete
* them.
*/
strbuf_addf(&t->path, "%s/tmp_objdir-%s-XXXXXX", get_object_directory(), prefix);
/*
* Grow the strbuf beyond any filename we expect to be placed in it.
@ -269,6 +281,13 @@ int tmp_objdir_migrate(struct tmp_objdir *t)
if (!t)
return 0;
if (t->prev_odb) {
if (the_repository->objects->odb->will_destroy)
BUG("migrating an ODB that was marked for destruction");
restore_primary_odb(t->prev_odb, t->path.buf);
t->prev_odb = NULL;
}
strbuf_addbuf(&src, &t->path);
strbuf_addstr(&dst, get_object_directory());
@ -292,3 +311,33 @@ void tmp_objdir_add_as_alternate(const struct tmp_objdir *t)
{
add_to_alternates_memory(t->path.buf);
}
void tmp_objdir_replace_primary_odb(struct tmp_objdir *t, int will_destroy)
{
if (t->prev_odb)
BUG("the primary object database is already replaced");
t->prev_odb = set_temporary_primary_odb(t->path.buf, will_destroy);
t->will_destroy = will_destroy;
}
struct tmp_objdir *tmp_objdir_unapply_primary_odb(void)
{
if (!the_tmp_objdir || !the_tmp_objdir->prev_odb)
return NULL;
restore_primary_odb(the_tmp_objdir->prev_odb, the_tmp_objdir->path.buf);
the_tmp_objdir->prev_odb = NULL;
return the_tmp_objdir;
}
void tmp_objdir_reapply_primary_odb(struct tmp_objdir *t, const char *old_cwd,
const char *new_cwd)
{
char *path;
path = reparent_relative_path(old_cwd, new_cwd, t->path.buf);
strbuf_reset(&t->path);
strbuf_addstr(&t->path, path);
free(path);
tmp_objdir_replace_primary_odb(t, t->will_destroy);
}

View File

@ -10,7 +10,7 @@
*
* Example:
*
* struct tmp_objdir *t = tmp_objdir_create();
* struct tmp_objdir *t = tmp_objdir_create("incoming");
* if (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&
* !tmp_objdir_migrate(t))
* printf("success!\n");
@ -22,9 +22,10 @@
struct tmp_objdir;
/*
* Create a new temporary object directory; returns NULL on failure.
* Create a new temporary object directory with the specified prefix;
* returns NULL on failure.
*/
struct tmp_objdir *tmp_objdir_create(void);
struct tmp_objdir *tmp_objdir_create(const char *prefix);
/*
* Return a list of environment strings, suitable for use with
@ -51,4 +52,26 @@ int tmp_objdir_destroy(struct tmp_objdir *);
*/
void tmp_objdir_add_as_alternate(const struct tmp_objdir *);
/*
* Replaces the writable object store in the current process with the temporary
* object directory and makes the former main object store an alternate.
* If will_destroy is nonzero, the object directory may not be migrated.
*/
void tmp_objdir_replace_primary_odb(struct tmp_objdir *, int will_destroy);
/*
* If the primary object database was replaced by a temporary object directory,
* restore it to its original value while keeping the directory contents around.
* Returns NULL if the primary object database was not replaced.
*/
struct tmp_objdir *tmp_objdir_unapply_primary_odb(void);
/*
* Reapplies the former primary temporary object database, after potentially
* changing its relative path.
*/
void tmp_objdir_reapply_primary_odb(struct tmp_objdir *, const char *old_cwd,
const char *new_cwd);
#endif /* TMP_OBJDIR_H */