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
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); return error("Could not stat '%s'", fullpath);
if (st.st_mtime > expire) if (st.st_mtime > expire)
return 0; return 0;
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) if (show_only || verbose)
printf("Removing stale temporary file %s\n", fullpath); printf("Removing stale temporary file %s\n", fullpath);
if (!show_only) if (!show_only)
unlink_or_warn(fullpath); unlink_or_warn(fullpath);
}
return 0; 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); strvec_push(&child.args, alt_shallow_file);
} }
tmp_objdir = tmp_objdir_create(); tmp_objdir = tmp_objdir_create("incoming");
if (!tmp_objdir) { if (!tmp_objdir) {
if (err_fd > 0) if (err_fd > 0)
close(err_fd); close(err_fd);

View File

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

View File

@ -680,6 +680,49 @@ void add_to_alternates_memory(const char *reference)
'\n', NULL, 0); '\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 * 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` * 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. */ /* Finalize a file on disk, and close it. */
static void close_loose_object(int fd) static void close_loose_object(int fd)
{ {
if (!the_repository->objects->odb->will_destroy) {
if (fsync_object_files) if (fsync_object_files)
fsync_or_die(fd, "loose object file"); fsync_or_die(fd, "loose object file");
}
if (close(fd) != 0) if (close(fd) != 0)
die_errno(_("error when closing loose object file")); 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 */ uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
struct oidtree *loose_objects_cache; 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, * Path to the alternative object store. If this is a relative path,
* it is relative to the current working directory. * 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); 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 * Populate and return the loose object cache array corresponding to the
* given object ID. * 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. */ /* Empty the loose object cache for the specified object directory. */
void odb_clear_loose_cache(struct object_directory *odb); 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 packed_git {
struct hashmap_entry packmap_ent; struct hashmap_entry packmap_ent;
struct packed_git *next; struct packed_git *next;

View File

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

2
refs.c
View File

@ -2145,7 +2145,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
break; break;
} }
if (getenv(GIT_QUARANTINE_ENVIRONMENT)) { if (refs->repo->objects->odb->disable_ref_updates) {
strbuf_addstr(err, strbuf_addstr(err,
_("ref updates forbidden inside quarantine environment")); _("ref updates forbidden inside quarantine environment"));
return -1; 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, expand_base_dir(&repo->objects->odb->path, o->object_dir,
repo->commondir, "objects"); repo->commondir, "objects");
repo->objects->odb->disable_ref_updates = o->disable_ref_updates;
free(repo->objects->alternate_db); free(repo->objects->alternate_db);
repo->objects->alternate_db = xstrdup_or_null(o->alternate_db); repo->objects->alternate_db = xstrdup_or_null(o->alternate_db);
expand_base_dir(&repo->graft_file, o->graft_file, 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 *graft_file;
const char *index_file; const char *index_file;
const char *alternate_db; const char *alternate_db;
int disable_ref_updates;
}; };
void repo_set_gitdir(struct repository *repo, const char *root, void repo_set_gitdir(struct repository *repo, const char *root,

View File

@ -1,5 +1,6 @@
#include "cache.h" #include "cache.h"
#include "tmp-objdir.h" #include "tmp-objdir.h"
#include "chdir-notify.h"
#include "dir.h" #include "dir.h"
#include "sigchain.h" #include "sigchain.h"
#include "string-list.h" #include "string-list.h"
@ -11,6 +12,8 @@
struct tmp_objdir { struct tmp_objdir {
struct strbuf path; struct strbuf path;
struct strvec env; 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) if (t == the_tmp_objdir)
the_tmp_objdir = NULL; 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 * This may use malloc via strbuf_grow(), but we should
* have pre-grown t->path sufficiently so that this * 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) if (!on_signal)
tmp_objdir_free(t); tmp_objdir_free(t);
return err; return err;
} }
@ -121,7 +128,7 @@ static int setup_tmp_objdir(const char *root)
return ret; return ret;
} }
struct tmp_objdir *tmp_objdir_create(void) struct tmp_objdir *tmp_objdir_create(const char *prefix)
{ {
static int installed_handlers; static int installed_handlers;
struct tmp_objdir *t; struct tmp_objdir *t;
@ -129,11 +136,16 @@ struct tmp_objdir *tmp_objdir_create(void)
if (the_tmp_objdir) if (the_tmp_objdir)
BUG("only one tmp_objdir can be used at a time"); 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); strbuf_init(&t->path, 0);
strvec_init(&t->env); 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. * 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) if (!t)
return 0; 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_addbuf(&src, &t->path);
strbuf_addstr(&dst, get_object_directory()); 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); 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: * 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)) && * if (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) &&
* !tmp_objdir_migrate(t)) * !tmp_objdir_migrate(t))
* printf("success!\n"); * printf("success!\n");
@ -22,9 +22,10 @@
struct tmp_objdir; 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 * 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 *); 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 */ #endif /* TMP_OBJDIR_H */