summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2007-01-02 19:19:05 (GMT)
committerJunio C Hamano <junkio@cox.net>2007-01-03 09:22:35 (GMT)
commit1084b845d9d77bcb2e8255636358dd0dc35360a5 (patch)
tree4605342374bdb48b94ddf67fe0f7a94714ad2d1e
parente6d40d65df07059fc655fabe62fa5b575ead7815 (diff)
downloadgit-1084b845d9d77bcb2e8255636358dd0dc35360a5.zip
git-1084b845d9d77bcb2e8255636358dd0dc35360a5.tar.gz
git-1084b845d9d77bcb2e8255636358dd0dc35360a5.tar.bz2
Fix infinite loop when deleting multiple packed refs.
It was stupid to link the same element twice to lock_file_list and end up in a loop, so we certainly need a fix. But it is not like we are taking a lock on multiple files in this case. It is just that we leave the linked element on the list even after commit_lock_file() successfully removes the cruft. We cannot remove the list element in commit_lock_file(); if we are interrupted in the middle of list manipulation, the call to remove_lock_file_on_signal() will happen with a broken list structure pointed by lock_file_list, which would cause the cruft to remain, so not removing the list element is the right thing to do. Instead we should be reusing the element already on the list. There is already a code for that in lock_file() function in lockfile.c. The code checks lk->next and the element is linked only when it is not already on the list -- which is incorrect for the last element on the list (which has NULL in its next field), but if you read the check as "is this element already on the list?" it actually makes sense. We do not want to link it on the list again, nor we would want to set up signal/atexit over and over. Signed-off-by: Junio C Hamano <junkio@cox.net>
-rw-r--r--cache.h1
-rw-r--r--lockfile.c7
-rw-r--r--refs.c1
3 files changed, 7 insertions, 2 deletions
diff --git a/cache.h b/cache.h
index f2ec5c8..a0e9727 100644
--- a/cache.h
+++ b/cache.h
@@ -174,6 +174,7 @@ extern int refresh_cache(unsigned int flags);
struct lock_file {
struct lock_file *next;
+ char on_list;
char filename[PATH_MAX];
};
extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
diff --git a/lockfile.c b/lockfile.c
index 2a2fea3..143d7d8 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -28,9 +28,12 @@ static int lock_file(struct lock_file *lk, const char *path)
sprintf(lk->filename, "%s.lock", path);
fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= fd) {
- if (!lk->next) {
+ if (!lk->on_list) {
lk->next = lock_file_list;
lock_file_list = lk;
+ lk->on_list = 1;
+ }
+ if (lock_file_list) {
signal(SIGINT, remove_lock_file_on_signal);
atexit(remove_lock_file);
}
@@ -38,6 +41,8 @@ static int lock_file(struct lock_file *lk, const char *path)
return error("cannot fix permission bits on %s",
lk->filename);
}
+ else
+ lk->filename[0] = 0;
return fd;
}
diff --git a/refs.c b/refs.c
index 0e156c5..1549f2a 100644
--- a/refs.c
+++ b/refs.c
@@ -644,7 +644,6 @@ static int repack_without_ref(const char *refname)
}
if (!found)
return 0;
- memset(&packlock, 0, sizeof(packlock));
fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
if (fd < 0)
return error("cannot delete '%s' from packed refs", refname);