Commit 69adb781e17f77b19d66613ad7e52c38d6ac64e1

Edward Thomson 2015-08-03T08:33:53

Merge pull request #3325 from libgit2/cmn/filebuf-rename-error filebuf: remove lockfile upon rename errors

diff --git a/src/filebuf.c b/src/filebuf.c
index 848ac34..838f4b4 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -101,7 +101,7 @@ void git_filebuf_cleanup(git_filebuf *file)
 	if (file->fd_is_open && file->fd >= 0)
 		p_close(file->fd);
 
-	if (file->fd_is_open && file->path_lock && git_path_exists(file->path_lock))
+	if (file->created_lock && !file->did_rename && file->path_lock && git_path_exists(file->path_lock))
 		p_unlink(file->path_lock);
 
 	if (file->compute_digest) {
@@ -258,6 +258,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
 			goto cleanup;
 		}
 		file->fd_is_open = true;
+		file->created_lock = true;
 
 		/* No original path */
 		file->path_original = NULL;
@@ -281,6 +282,8 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
 		/* open the file for locking */
 		if ((error = lock_file(file, flags, mode)) < 0)
 			goto cleanup;
+
+		file->created_lock = true;
 	}
 
 	return 0;
@@ -340,6 +343,8 @@ int git_filebuf_commit(git_filebuf *file)
 		goto on_error;
 	}
 
+	file->did_rename = true;
+
 	git_filebuf_cleanup(file);
 	return 0;
 
diff --git a/src/filebuf.h b/src/filebuf.h
index 2bd18dc..f4d255b 100644
--- a/src/filebuf.h
+++ b/src/filebuf.h
@@ -44,6 +44,8 @@ struct git_filebuf {
 	size_t buf_size, buf_pos;
 	git_file fd;
 	bool fd_is_open;
+	bool created_lock;
+	bool did_rename;
 	bool do_not_buffer;
 	int last_error;
 };
diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c
index 5a3e751..3f7dc85 100644
--- a/tests/core/filebuf.c
+++ b/tests/core/filebuf.c
@@ -124,3 +124,30 @@ void test_core_filebuf__umask(void)
 	cl_must_pass(p_unlink(test));
 }
 
+void test_core_filebuf__rename_error(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	char *dir = "subdir",  *test = "subdir/test", *test_lock = "subdir/test.lock";
+	int fd;
+
+#ifndef GIT_WIN32
+	cl_skip();
+#endif
+
+	cl_git_pass(p_mkdir(dir, 0666));
+	cl_git_mkfile(test, "dummy content");
+	fd = p_open(test, O_RDONLY);
+	cl_assert(fd > 0);
+	cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+
+	cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+	cl_assert_equal_i(true, git_path_exists(test_lock));
+
+	cl_git_fail(git_filebuf_commit(&file));
+	p_close(fd);
+
+	git_filebuf_cleanup(&file);
+
+	cl_assert_equal_i(false, git_path_exists(test_lock));
+}