Commit 1e60e5f42dbcf081ac7eece12a5eebab5871636f

Edward Thomson 2013-11-07T12:03:44

Allow callers to set mode on packfile creation

diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index fb55672..e4c03ad 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -20,6 +20,7 @@ typedef struct git_indexer git_indexer;
  *
  * @param out where to store the indexer instance
  * @param path to the directory where the packfile should be stored
+ * @param mode permissions to use creating packfile or 0 for defaults
  * @param odb object database from which to read base objects when
  * fixing thin packs. Pass NULL if no thin pack is expected (an error
  * will be returned if there are bases missing)
@@ -29,6 +30,7 @@ typedef struct git_indexer git_indexer;
 GIT_EXTERN(int) git_indexer_new(
 		git_indexer **out,
 		const char *path,
+		unsigned int mode,
 		git_odb *odb,
 		git_transfer_progress_callback progress_cb,
 		void *progress_cb_payload);
diff --git a/include/git2/pack.h b/include/git2/pack.h
index 4632699..52e7ada 100644
--- a/include/git2/pack.h
+++ b/include/git2/pack.h
@@ -119,6 +119,7 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid
  *
  * @param pb The packbuilder
  * @param path to the directory where the packfile and index should be stored
+ * @param mode permissions to use creating a packfile or 0 for defaults
  * @param progress_cb function to call with progress information from the indexer (optional)
  * @param progress_cb_payload payload for the progress callback (optional)
  *
@@ -127,6 +128,7 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid
 GIT_EXTERN(int) git_packbuilder_write(
 	git_packbuilder *pb,
 	const char *path,
+	unsigned int mode,
 	git_transfer_progress_callback progress_cb,
 	void *progress_cb_payload);
 
diff --git a/src/indexer.c b/src/indexer.c
index 90fb521..df1ce7c 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -37,6 +37,7 @@ struct git_indexer {
 	struct git_pack_header hdr;
 	struct git_pack_file *pack;
 	git_filebuf pack_file;
+	unsigned int mode;
 	git_off_t off;
 	git_off_t entry_start;
 	git_packfile_stream stream;
@@ -119,6 +120,7 @@ static int objects_cmp(const void *a, const void *b)
 int git_indexer_new(
 		git_indexer **out,
 		const char *prefix,
+		unsigned int mode,
 		git_odb *odb,
 		git_transfer_progress_callback progress_cb,
 		void *progress_payload)
@@ -133,6 +135,7 @@ int git_indexer_new(
 	idx->odb = odb;
 	idx->progress_cb = progress_cb;
 	idx->progress_payload = progress_payload;
+	idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
 	git_hash_ctx_init(&idx->trailer);
 
 	error = git_buf_joinpath(&path, prefix, suff);
@@ -141,7 +144,7 @@ int git_indexer_new(
 
 	error = git_filebuf_open(&idx->pack_file, path.ptr,
 		GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
-		GIT_PACK_FILE_MODE);
+		idx->mode);
 	git_buf_free(&path);
 	if (error < 0)
 		goto cleanup;
@@ -905,7 +908,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
 		return -1;
 
 	if (git_filebuf_open(&index_file, filename.ptr,
-		GIT_FILEBUF_HASH_CONTENTS, GIT_PACK_FILE_MODE) < 0)
+		GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0)
 		goto on_error;
 
 	/* Write out the header */
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 12f4591..fd2ca0f 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -558,7 +558,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
 	GITERR_CHECK_ALLOC(writepack);
 
 	if (git_indexer_new(&writepack->indexer,
-		backend->pack_folder, odb, progress_cb, progress_payload) < 0) {
+		backend->pack_folder, 0, odb, progress_cb, progress_payload) < 0) {
 		git__free(writepack);
 		return -1;
 	}
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 91811b9..9967cab 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -1248,6 +1248,7 @@ static int write_cb(void *buf, size_t len, void *payload)
 int git_packbuilder_write(
 	git_packbuilder *pb,
 	const char *path,
+	unsigned int mode,
 	git_transfer_progress_callback progress_cb,
 	void *progress_cb_payload)
 {
@@ -1258,7 +1259,7 @@ int git_packbuilder_write(
 	PREPARE_PACK;
 
 	if (git_indexer_new(
-		&indexer, path, pb->odb, progress_cb, progress_cb_payload) < 0)
+		&indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0)
 		return -1;
 
 	ctx.indexer = indexer;
diff --git a/tests-clar/pack/indexer.c b/tests-clar/pack/indexer.c
index d795330..07963a9 100644
--- a/tests-clar/pack/indexer.c
+++ b/tests-clar/pack/indexer.c
@@ -48,7 +48,7 @@ void test_pack_indexer__out_of_order(void)
 	git_indexer *idx;
 	git_transfer_progress stats;
 
-	cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL));
+	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
 	cl_git_pass(git_indexer_append(idx, out_of_order_pack, out_of_order_pack_len, &stats));
 	cl_git_pass(git_indexer_commit(idx, &stats));
 
@@ -75,7 +75,7 @@ void test_pack_indexer__fix_thin(void)
 	git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
 	cl_assert(!git_oid_cmp(&id, &should_id));
 
-	cl_git_pass(git_indexer_new(&idx, ".", odb, NULL, NULL));
+	cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
 	cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
 	cl_git_pass(git_indexer_commit(idx, &stats));
 
@@ -108,7 +108,7 @@ void test_pack_indexer__fix_thin(void)
 
 		cl_git_pass(p_stat(name, &st));
 
-		cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL));
+		cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
 		read = p_read(fd, buffer, sizeof(buffer));
 		cl_assert(read != -1);
 		p_close(fd);
diff --git a/tests-clar/pack/packbuilder.c b/tests-clar/pack/packbuilder.c
index dd028a1..54d1e80 100644
--- a/tests-clar/pack/packbuilder.c
+++ b/tests-clar/pack/packbuilder.c
@@ -1,5 +1,6 @@
 #include "clar_libgit2.h"
 #include "fileops.h"
+#include "pack.h"
 #include "hash.h"
 #include "iterator.h"
 #include "vector.h"
@@ -92,7 +93,7 @@ void test_pack_packbuilder__create_pack(void)
 
 	seed_packbuilder();
 
-	cl_git_pass(git_indexer_new(&_indexer, ".", NULL, NULL, NULL));
+	cl_git_pass(git_indexer_new(&_indexer, ".", 0, NULL, NULL, NULL));
 	cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats));
 	cl_git_pass(git_indexer_commit(_indexer, &stats));
 
@@ -134,12 +135,55 @@ void test_pack_packbuilder__get_hash(void)
 
 	seed_packbuilder();
 
-	git_packbuilder_write(_packbuilder, ".", NULL, NULL);
+	git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL);
 	git_oid_fmt(hex, git_packbuilder_hash(_packbuilder));
 
 	cl_assert_equal_s(hex, "80e61eb315239ef3c53033e37fee43b744d57122");
 }
 
+static void test_write_pack_permission(mode_t given, mode_t expected)
+{
+	struct stat statbuf;
+	mode_t mask, os_mask;
+
+	seed_packbuilder();
+
+	git_packbuilder_write(_packbuilder, ".", given, NULL, NULL);
+
+	/* Windows does not return group/user bits from stat,
+	* files are never executable.
+	*/
+#ifdef GIT_WIN32
+	os_mask = 0600;
+#else
+	os_mask = 0777;
+#endif
+
+	mask = p_umask(0);
+	p_umask(mask);
+
+	cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.idx", &statbuf));
+	cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+
+	cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.pack", &statbuf));
+	cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+}
+
+void test_pack_packbuilder__permissions_standard(void)
+{
+	test_write_pack_permission(0, GIT_PACK_FILE_MODE);
+}
+
+void test_pack_packbuilder__permissions_readonly(void)
+{
+	test_write_pack_permission(0444, 0444);
+}
+
+void test_pack_packbuilder__permissions_readwrite(void)
+{
+	test_write_pack_permission(0666, 0666);
+}
+
 static git_transfer_progress stats;
 static int foreach_cb(void *buf, size_t len, void *payload)
 {
@@ -153,7 +197,7 @@ void test_pack_packbuilder__foreach(void)
 	git_indexer *idx;
 
 	seed_packbuilder();
-	cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL));
+	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
 	cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx));
 	cl_git_pass(git_indexer_commit(idx, &stats));
 	git_indexer_free(idx);