Commit 4d7ec76c42d9bcf2ed4f59e0efff2f4e6e810808

Edward Thomson 2021-12-12T09:19:25

odb: add git_odb_loose_backend_options Move the arguments to `git_odb_loose` into an options structure.

diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h
index d93ac73..d65aeec 100644
--- a/include/git2/odb_backend.h
+++ b/include/git2/odb_backend.h
@@ -34,25 +34,55 @@ GIT_BEGIN_DECL
  */
 GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
 
+typedef enum {
+	GIT_ODB_BACKEND_LOOSE_FSYNC = (1 << 0)
+} git_odb_backend_loose_flag_t;
+
+/** Options for configuring a loose object backend. */
+typedef struct {
+	unsigned int version; /**< version for the struct */
+
+	/** A combination of the `git_odb_backend_loose_flag_t` types. */
+	uint32_t flags;
+
+	/**
+	 * zlib compression level to use (0-9), where 1 is the fastest
+	 * at the expense of larger files, and 9 produces the best
+	 * compression at the expense of speed.  0 indicates that no
+	 * compression should be performed.  -1 is the default (currently
+	 * optimizing for speed).
+	 */
+	int compression_level;
+
+	/** Permissions to use creating a directory or 0 for defaults */
+	unsigned int dir_mode;
+
+	/** Permissions to use creating a file or 0 for defaults */
+	unsigned int file_mode;
+} git_odb_backend_loose_options;
+
+/* The current version of the diff options structure */
+#define GIT_ODB_BACKEND_LOOSE_OPTIONS_VERSION 1
+
+/* Stack initializer for diff options.  Alternatively use
+ * `git_diff_options_init` programmatic initialization.
+ */
+#define GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT \
+	{ GIT_ODB_BACKEND_LOOSE_OPTIONS_VERSION, 0, -1 }
+
 /**
  * Create a backend for loose objects
  *
  * @param out location to store the odb backend pointer
  * @param objects_dir the Git repository's objects directory
- * @param compression_level zlib compression level to use
- * @param do_fsync whether to do an fsync() after writing
- * @param dir_mode permissions to use creating a directory or 0 for defaults
- * @param file_mode permissions to use creating a file or 0 for defaults
+ * @param opts options for the loose object backend or NULL
  *
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_odb_backend_loose(
 	git_odb_backend **out,
 	const char *objects_dir,
-	int compression_level,
-	int do_fsync,
-	unsigned int dir_mode,
-	unsigned int file_mode);
+	git_odb_backend_loose_options *opts);
 
 /**
  * Create a backend out of a single packfile
diff --git a/src/libgit2/odb.c b/src/libgit2/odb.c
index 4e2e07a..1f9911d 100644
--- a/src/libgit2/odb.c
+++ b/src/libgit2/odb.c
@@ -625,6 +625,7 @@ int git_odb__add_default_backends(
 	struct stat st;
 	ino_t inode;
 	git_odb_backend *loose, *packed;
+	git_odb_backend_loose_options loose_opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
 
 	/* TODO: inodes are not really relevant on Win32, so we need to find
 	 * a cross-platform workaround for this */
@@ -659,8 +660,11 @@ int git_odb__add_default_backends(
 	git_mutex_unlock(&db->lock);
 #endif
 
+	if (db->do_fsync)
+		loose_opts.flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
+
 	/* add the loose object backend */
-	if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 ||
+	if (git_odb_backend_loose(&loose, objects_dir, &loose_opts) < 0 ||
 		add_backend_internal(db, loose, git_odb__loose_priority, as_alternates, inode) < 0)
 		return -1;
 
diff --git a/src/libgit2/odb_loose.c b/src/libgit2/odb_loose.c
index f5013df..1885fa2 100644
--- a/src/libgit2/odb_loose.c
+++ b/src/libgit2/odb_loose.c
@@ -46,10 +46,7 @@ typedef struct {
 typedef struct loose_backend {
 	git_odb_backend parent;
 
-	int object_zlib_level; /** loose object zlib compression level. */
-	int fsync_object_files; /** loose object file fsync flag. */
-	mode_t object_file_mode;
-	mode_t object_dir_mode;
+	git_odb_backend_loose_options options;
 
 	size_t objects_dirlen;
 	char objects_dir[GIT_FLEX_ARRAY];
@@ -100,7 +97,9 @@ static int object_file_name(
 static int object_mkdir(const git_str *name, const loose_backend *be)
 {
 	return git_futils_mkdir_relative(
-		name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
+		name->ptr + be->objects_dirlen,
+		be->objects_dir,
+		be->options.dir_mode,
 		GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
 }
 
@@ -827,9 +826,10 @@ static void loose_backend__writestream_free(git_odb_stream *_stream)
 static int filebuf_flags(loose_backend *backend)
 {
 	int flags = GIT_FILEBUF_TEMPORARY |
-		(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT);
+		(backend->options.compression_level << GIT_FILEBUF_DEFLATE_SHIFT);
 
-	if (backend->fsync_object_files || git_repository__fsync_gitdir)
+	if ((backend->options.flags & GIT_ODB_BACKEND_LOOSE_FSYNC) ||
+	    git_repository__fsync_gitdir)
 		flags |= GIT_FILEBUF_FSYNC;
 
 	return flags;
@@ -865,7 +865,7 @@ static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backe
 
 	if (git_str_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
 		git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend),
-			backend->object_file_mode) < 0 ||
+			backend->options.file_mode) < 0 ||
 		stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
 	{
 		git_filebuf_cleanup(&stream->fbuf);
@@ -1083,7 +1083,7 @@ static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, c
 
 	if (git_str_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
 		git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
-			backend->object_file_mode) < 0)
+			backend->options.file_mode) < 0)
 	{
 		error = -1;
 		goto cleanup;
@@ -1126,13 +1126,31 @@ static void loose_backend__free(git_odb_backend *_backend)
 	git__free(_backend);
 }
 
+static void normalize_options(
+	git_odb_backend_loose_options *opts,
+	const git_odb_backend_loose_options *given_opts)
+{
+	git_odb_backend_loose_options init = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+	if (given_opts)
+		memcpy(opts, given_opts, sizeof(git_odb_backend_loose_options));
+	else
+		memcpy(opts, &init, sizeof(git_odb_backend_loose_options));
+
+	if (opts->compression_level < 0)
+		opts->compression_level = Z_BEST_SPEED;
+
+	if (opts->dir_mode == 0)
+		opts->dir_mode = GIT_OBJECT_DIR_MODE;
+
+	if (opts->file_mode == 0)
+		opts->file_mode = GIT_OBJECT_FILE_MODE;
+}
+
 int git_odb_backend_loose(
 	git_odb_backend **backend_out,
 	const char *objects_dir,
-	int compression_level,
-	int do_fsync,
-	unsigned int dir_mode,
-	unsigned int file_mode)
+	git_odb_backend_loose_options *opts)
 {
 	loose_backend *backend;
 	size_t objects_dirlen, alloclen;
@@ -1150,22 +1168,11 @@ int git_odb_backend_loose(
 	backend->parent.version = GIT_ODB_BACKEND_VERSION;
 	backend->objects_dirlen = objects_dirlen;
 	memcpy(backend->objects_dir, objects_dir, objects_dirlen);
+
 	if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
 		backend->objects_dir[backend->objects_dirlen++] = '/';
 
-	if (compression_level < 0)
-		compression_level = Z_BEST_SPEED;
-
-	if (dir_mode == 0)
-		dir_mode = GIT_OBJECT_DIR_MODE;
-
-	if (file_mode == 0)
-		file_mode = GIT_OBJECT_FILE_MODE;
-
-	backend->object_zlib_level = compression_level;
-	backend->fsync_object_files = do_fsync;
-	backend->object_dir_mode = dir_mode;
-	backend->object_file_mode = file_mode;
+	normalize_options(&backend->options, opts);
 
 	backend->parent.read = &loose_backend__read;
 	backend->parent.write = &loose_backend__write;
diff --git a/tests/libgit2/odb/loose.c b/tests/libgit2/odb/loose.c
index 5da1f09..20b329f 100644
--- a/tests/libgit2/odb/loose.c
+++ b/tests/libgit2/odb/loose.c
@@ -196,6 +196,7 @@ static void test_write_object_permission(
 	git_oid oid;
 	struct stat statbuf;
 	mode_t mask, os_mask;
+	git_odb_backend_loose_options opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
 
 	/* Windows does not return group/user bits from stat,
 	* files are never executable.
@@ -209,8 +210,11 @@ static void test_write_object_permission(
 	mask = p_umask(0);
 	p_umask(mask);
 
+	opts.dir_mode = dir_mode;
+	opts.file_mode = file_mode;
+
 	cl_git_pass(git_odb_new(&odb, NULL));
-	cl_git_pass(git_odb_backend_loose(&backend, "test-objects", -1, 0, dir_mode, file_mode));
+	cl_git_pass(git_odb_backend_loose(&backend, "test-objects", &opts));
 	cl_git_pass(git_odb_add_backend(odb, backend, 1));
 	cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJECT_BLOB));
 
@@ -243,9 +247,16 @@ static void write_object_to_loose_odb(int fsync)
 	git_odb *odb;
 	git_odb_backend *backend;
 	git_oid oid;
+	git_odb_backend_loose_options opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+	if (fsync)
+		opts.flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
+
+	opts.dir_mode = 0777;
+	opts.file_mode = 0666;
 
 	cl_git_pass(git_odb_new(&odb, NULL));
-	cl_git_pass(git_odb_backend_loose(&backend, "test-objects", -1, fsync, 0777, 0666));
+	cl_git_pass(git_odb_backend_loose(&backend, "test-objects", &opts));
 	cl_git_pass(git_odb_add_backend(odb, backend, 1));
 	cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJECT_BLOB));
 	git_odb_free(odb);
diff --git a/tests/libgit2/odb/sorting.c b/tests/libgit2/odb/sorting.c
index 36c4507..33ce289 100644
--- a/tests/libgit2/odb/sorting.c
+++ b/tests/libgit2/odb/sorting.c
@@ -79,10 +79,11 @@ void test_odb_sorting__override_default_backend_priority(void)
 {
 	git_odb *new_odb;
 	git_odb_backend *loose, *packed, *backend;
+
 	cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, 5));
 	cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, 3));
 	git_odb_backend_pack(&packed, "./testrepo.git/objects");
-	git_odb_backend_loose(&loose, "./testrepo.git/objects", -1, 0, 0, 0);
+	git_odb_backend_loose(&loose, "./testrepo.git/objects", NULL);
 
 	cl_git_pass(git_odb_open(&new_odb, cl_fixture("testrepo.git/objects"), NULL));
 	cl_assert_equal_sz(2, git_odb_num_backends(new_odb));