Commit ce5553d48b8ba3510dd5032dfbfd161fb801cd77

Carlos Martín Nieto 2016-03-10T22:01:09

refdb: bubble up locked files on the read side On Windows we can find locked files even when reading a reference or the packed-refs file. Bubble up the error in this case as well to allow callers on Windows to retry more intelligently.

diff --git a/src/path.c b/src/path.c
index f91e422..2b1a962 100644
--- a/src/path.c
+++ b/src/path.c
@@ -644,6 +644,10 @@ int git_path_set_error(int errno_value, const char *path, const char *action)
 		giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
 		return GIT_EEXISTS;
 
+	case EACCES:
+		giterr_set(GITERR_OS, "Failed %s - '%s' is locked", action, path);
+		return GIT_ELOCKED;
+
 	default:
 		giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
 		return -1;
diff --git a/src/refdb.c b/src/refdb.c
index debba12..85c8489 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -125,13 +125,15 @@ int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
 
 int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
 {
+	int error;
+
 	if (!db->backend || !db->backend->iterator) {
 		giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators");
 		return -1;
 	}
 
-	if (db->backend->iterator(out, db->backend, glob) < 0)
-		return -1;
+	if ((error = db->backend->iterator(out, db->backend, glob)) < 0)
+		return error;
 
 	GIT_REFCOUNT_INC(db);
 	(*out)->db = db;
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index ab309c8..7601aa0 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -326,12 +326,13 @@ static int refdb_fs_backend__exists(
 {
 	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	git_buf ref_path = GIT_BUF_INIT;
+	int error;
 
 	assert(backend);
 
-	if (packed_reload(backend) < 0 ||
-		git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0 ||
+		(error = git_buf_joinpath(&ref_path, backend->path, ref_name)) < 0)
+		return error;
 
 	*exists = git_path_isfile(ref_path.ptr) ||
 		(git_sortedcache_lookup(backend->refcache, ref_name) != NULL);
@@ -409,8 +410,8 @@ static int packed_lookup(
 	int error = 0;
 	struct packref *entry;
 
-	if (packed_reload(backend) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0)
+		return error;
 
 	if (git_sortedcache_rlock(backend->refcache) < 0)
 		return -1;
@@ -615,13 +616,14 @@ static int refdb_fs_backend__iterator_next_name(
 static int refdb_fs_backend__iterator(
 	git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
 {
+	int error;
 	refdb_fs_iter *iter;
 	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 
 	assert(backend);
 
-	if (packed_reload(backend) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0)
+		return error;
 
 	iter = git__calloc(1, sizeof(refdb_fs_iter));
 	GITERR_CHECK_ALLOC(iter);
@@ -674,16 +676,18 @@ static int reference_path_available(
 	int force)
 {
 	size_t i;
+	int error;
 
-	if (packed_reload(backend) < 0)
-		return -1;
+	if ((error = packed_reload(backend)) < 0)
+		return error;
 
 	if (!force) {
 		int exists;
 
-		if (refdb_fs_backend__exists(
-				&exists, (git_refdb_backend *)backend, new_ref) < 0)
-			return -1;
+		if ((error = refdb_fs_backend__exists(
+			&exists, (git_refdb_backend *)backend, new_ref)) < 0) {
+			return error;
+		}
 
 		if (exists) {
 			giterr_set(GITERR_REFERENCE,
@@ -1164,8 +1168,7 @@ static int refdb_fs_backend__write(
 
 	assert(backend);
 
-	error = reference_path_available(backend, ref->name, NULL, force);
-	if (error < 0)
+	if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
 		return error;
 
 	/* We need to perform the reflog append and old value check under the ref's lock */
@@ -1811,9 +1814,10 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co
 	 * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
 	 */
 	if (git_path_isdir(git_buf_cstr(&path))) {
-		if ((git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0))
-			error = -1;
-		else if (git_path_isdir(git_buf_cstr(&path))) {
+		if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
+			if (error == GIT_ENOTFOUND)
+				error = 0;
+		} else if (git_path_isdir(git_buf_cstr(&path))) {
 			giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
 				ref->name);
 			error = GIT_EDIRECTORY;
diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c
index d8dc77b..ae1a935 100644
--- a/tests/threads/refdb.c
+++ b/tests/threads/refdb.c
@@ -29,11 +29,14 @@ static void *iterate_refs(void *arg)
 	struct th_data *data = (struct th_data *) arg;
 	git_reference_iterator *i;
 	git_reference *ref;
-	int count = 0;
+	int count = 0, error;
 	git_repository *repo;
 
 	cl_git_pass(git_repository_open(&repo, data->path));
-	cl_git_pass(git_reference_iterator_new(&i, repo));
+	do {
+		error = git_reference_iterator_new(&i, repo);
+	} while (error == GIT_ELOCKED);
+	cl_git_pass(error);
 
 	for (count = 0; !git_reference_next(&ref, i); ++count) {
 		cl_assert(ref != NULL);
@@ -61,11 +64,17 @@ static void *create_refs(void *arg)
 
 	cl_git_pass(git_repository_open(&repo, data->path));
 
-	cl_git_pass(git_reference_name_to_id(&head, repo, "HEAD"));
+	do {
+		error = git_reference_name_to_id(&head, repo, "HEAD");
+	} while (error == GIT_ELOCKED);
+	cl_git_pass(error);
 
 	for (i = 0; i < 10; ++i) {
 		p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i);
-		cl_git_pass(git_reference_create(&ref[i], repo, name, &head, 0, NULL));
+		do {
+			error = git_reference_create(&ref[i], repo, name, &head, 0, NULL);
+		} while (error == GIT_ELOCKED);
+		cl_git_pass(error);
 
 		if (i == 5) {
 			git_refdb *refdb;