[PATCH] Make locked paths absolute when current directory is changed

Subsystems: the rest

STALE3744d

13 messages, 4 authors, 2016-06-15 · open the first message on its own page

[PATCH] Make locked paths absolute when current directory is changed

From: Nguyễn Thái Ngọc Duy <hidden>
Date: 2016-06-15 23:01:58

Locked paths are saved in a linked list so that if something wrong
happens, *.lock are removed. This works fine if we keep cwd the same,
which is true 99% of time except:

 - update-index and read-tree hold the lock on $GIT_DIR/index really
   early, then later on may call setup_work_tree() to move cwd.

 - Suppose a lock is being held (e.g. by "git add") then somewhere
   down the line, somebody calls real_path (e.g. "link_alt_odb_entry"),
   which temporarily moves cwd away and back.

During that time when cwd is moved (either permanently or temporarily)
and we decide to die(), attempts to remove relative *.lock will fail,
and the next operation will complain that some files are still locked.

Avoid this case by turning relative paths to absolute when chdir() is
called (or soon to be called, in setup_git_directory_gently case).

Reported-by: Yue Lin Ho <redacted>
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
 It occurred to me while writing this commit message that it would be
 better if we can unlink via a file descriptor so we don't have to
 convert paths. But I'm not sure about the availability of unlinkat(),
 especially on Windows.

 abspath.c     |  2 +-
 cache.h       |  6 ++++++
 git.c         |  6 +++---
 lockfile.c    | 16 ++++++++++++++++
 path.c        |  4 ++--
 run-command.c |  2 +-
 setup.c       |  3 ++-
 unix-socket.c |  2 +-
 8 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/abspath.c b/abspath.c
index ca33558..78c963f 100644
--- a/abspath.c
+++ b/abspath.c
@@ -87,7 +87,7 @@ static const char *real_path_internal(const char *path, int die_on_error)
 					goto error_out;
 			}
 
-			if (chdir(buf)) {
+			if (chdir_safe(buf)) {
 				if (die_on_error)
 					die_errno("Could not switch to '%s'", buf);
 				else
diff --git a/cache.h b/cache.h
index 44aa439..d3f2596 100644
--- a/cache.h
+++ b/cache.h
@@ -564,6 +564,12 @@ extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
 extern void update_index_if_able(struct index_state *, struct lock_file *);
+extern void make_locked_paths_absolute(void);
+static inline int chdir_safe(const char *path)
+{
+	make_locked_paths_absolute();
+	return chdir(path);
+}
 
 extern int hold_locked_index(struct lock_file *, int);
 extern int commit_locked_index(struct lock_file *);
diff --git a/git.c b/git.c
index 5b6c761..27766c3 100644
--- a/git.c
+++ b/git.c
@@ -48,7 +48,7 @@ static void save_env(void)
 static void restore_env(void)
 {
 	int i;
-	if (*orig_cwd && chdir(orig_cwd))
+	if (*orig_cwd && chdir_safe(orig_cwd))
 		die_errno("could not move to %s", orig_cwd);
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 		if (orig_env[i])
@@ -206,7 +206,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 				fprintf(stderr, "No directory given for -C.\n" );
 				usage(git_usage_string);
 			}
-			if (chdir((*argv)[1]))
+			if (chdir_safe((*argv)[1]))
 				die_errno("Cannot change to '%s'", (*argv)[1]);
 			if (envchanged)
 				*envchanged = 1;
@@ -292,7 +292,7 @@ static int handle_alias(int *argcp, const char ***argv)
 		ret = 1;
 	}
 
-	if (subdir && chdir(subdir))
+	if (subdir && chdir_safe(subdir))
 		die_errno("Cannot change to '%s'", subdir);
 
 	errno = saved_errno;
diff --git a/lockfile.c b/lockfile.c
index 8fbcb6a..a70d107 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -280,3 +280,19 @@ void rollback_lock_file(struct lock_file *lk)
 	}
 	lk->filename[0] = 0;
 }
+
+void make_locked_paths_absolute(void)
+{
+	struct lock_file *lk;
+	const char *abspath;
+	for (lk = lock_file_list; lk != NULL; lk = lk->next) {
+		if (!lk->filename[0] || lk->filename[0] == '/')
+			continue;
+		abspath = absolute_path(lk->filename);
+		if (strlen(abspath) >= sizeof(lk->filename))
+			warning("locked path %s is relative when current directory "
+				"is changed", lk->filename);
+		else
+			strcpy(lk->filename, abspath);
+	}
+}
diff --git a/path.c b/path.c
index bc804a3..9e8e101 100644
--- a/path.c
+++ b/path.c
@@ -372,11 +372,11 @@ const char *enter_repo(const char *path, int strict)
 		gitfile = read_gitfile(used_path) ;
 		if (gitfile)
 			strcpy(used_path, gitfile);
-		if (chdir(used_path))
+		if (chdir_safe(used_path))
 			return NULL;
 		path = validated_path;
 	}
-	else if (chdir(path))
+	else if (chdir_safe(path))
 		return NULL;
 
 	if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
diff --git a/run-command.c b/run-command.c
index be07d4a..55f360e 100644
--- a/run-command.c
+++ b/run-command.c
@@ -399,7 +399,7 @@ fail_pipe:
 			close(cmd->out);
 		}
 
-		if (cmd->dir && chdir(cmd->dir))
+		if (cmd->dir && chdir_safe(cmd->dir))
 			die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
 			    cmd->dir);
 		if (cmd->env) {
diff --git a/setup.c b/setup.c
index 0a22f8b..921045e 100644
--- a/setup.c
+++ b/setup.c
@@ -290,7 +290,7 @@ void setup_work_tree(void)
 	git_dir = get_git_dir();
 	if (!is_absolute_path(git_dir))
 		git_dir = real_path(get_git_dir());
-	if (!work_tree || chdir(work_tree))
+	if (!work_tree || chdir_safe(work_tree))
 		die("This operation must be run in a work tree");
 
 	/*
@@ -636,6 +636,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 		die_errno("Unable to read current working directory");
 	offset = len = strlen(cwd);
 
+	make_locked_paths_absolute();
 	/*
 	 * If GIT_DIR is set explicitly, we're not going
 	 * to do any discovery, but we still do repository
diff --git a/unix-socket.c b/unix-socket.c
index 01f119f..eeb8007 100644
--- a/unix-socket.c
+++ b/unix-socket.c
@@ -12,7 +12,7 @@ static int unix_stream_socket(void)
 static int chdir_len(const char *orig, int len)
 {
 	char *path = xmemdupz(orig, len);
-	int r = chdir(path);
+	int r = chdir_safe(path);
 	free(path);
 	return r;
 }
-- 
1.9.1.346.ga2b5940

Re: [PATCH] Make locked paths absolute when current directory is changed

From: Johannes Sixt <hidden>
Date: 2016-06-15 23:01:58

Am 18.07.2014 15:08, schrieb Nguyễn Thái Ngọc Duy:
quoted hunk
diff --git a/lockfile.c b/lockfile.c
index 8fbcb6a..a70d107 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -280,3 +280,19 @@ void rollback_lock_file(struct lock_file *lk)
 	}
 	lk->filename[0] = 0;
 }
+
+void make_locked_paths_absolute(void)
+{
+	struct lock_file *lk;
+	const char *abspath;
+	for (lk = lock_file_list; lk != NULL; lk = lk->next) {
+		if (!lk->filename[0] || lk->filename[0] == '/')
Please use is_absolute_path().
+			continue;
+		abspath = absolute_path(lk->filename);
+		if (strlen(abspath) >= sizeof(lk->filename))
+			warning("locked path %s is relative when current directory "
+				"is changed", lk->filename);
+		else
+			strcpy(lk->filename, abspath);
+	}
+}
quoted hunk
--- a/run-command.c
+++ b/run-command.c
@@ -399,7 +399,7 @@ fail_pipe:
 			close(cmd->out);
 		}
 
-		if (cmd->dir && chdir(cmd->dir))
+		if (cmd->dir && chdir_safe(cmd->dir))
This one shouldn't be necessary: It's in the child, and the child
process does not release the locks; see the check for the owner in
remove_lock_file.
 			die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
 			    cmd->dir);
 		if (cmd->env) {
-- Hannes

[PATCH v2 1/2] lockfile.c: remove PATH_MAX limitation (except in resolve_symlink)

From: Nguyễn Thái Ngọc Duy <hidden>
Date: 2016-06-15 23:01:59

Something extra is, because struct lock_file is usually used as static
variables in many places. This patch reduces bss section by about 80k
bytes (or 23%) on Linux.

Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
 This helps remove the length check in v1 of the next patch.

 cache.h    |  2 +-
 lockfile.c | 56 ++++++++++++++++++++++++++++++++------------------------
 2 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/cache.h b/cache.h
index 44aa439..9ecb636 100644
--- a/cache.h
+++ b/cache.h
@@ -554,7 +554,7 @@ struct lock_file {
 	int fd;
 	pid_t owner;
 	char on_list;
-	char filename[PATH_MAX];
+	char *filename;
 };
 #define LOCK_DIE_ON_ERROR 1
 #define LOCK_NODEREF 2
diff --git a/lockfile.c b/lockfile.c
index 8fbcb6a..968b28f 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -7,13 +7,19 @@
 static struct lock_file *lock_file_list;
 static const char *alternate_index_output;
 
+static void clear_filename(struct lock_file *lk)
+{
+	free(lk->filename);
+	lk->filename = NULL;
+}
+
 static void remove_lock_file(void)
 {
 	pid_t me = getpid();
 
 	while (lock_file_list) {
 		if (lock_file_list->owner == me &&
-		    lock_file_list->filename[0]) {
+		    lock_file_list->filename) {
 			if (lock_file_list->fd >= 0)
 				close(lock_file_list->fd);
 			unlink_or_warn(lock_file_list->filename);
@@ -77,10 +83,16 @@ static char *last_path_elm(char *p)
  * Always returns p.
  */
 
-static char *resolve_symlink(char *p, size_t s)
+static char *resolve_symlink(const char *in)
 {
+	static char p[PATH_MAX];
+	size_t s = sizeof(p);
 	int depth = MAXDEPTH;
 
+	if (strlen(in) >= sizeof(p))
+		return NULL;
+	strcpy(p, in);
+
 	while (depth--) {
 		char link[PATH_MAX];
 		int link_len = readlink(p, link, sizeof(link));
@@ -124,17 +136,12 @@ static char *resolve_symlink(char *p, size_t s)
 
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-	/*
-	 * subtract 5 from size to make sure there's room for adding
-	 * ".lock" for the lock file name
-	 */
-	static const size_t max_path_len = sizeof(lk->filename) - 5;
-
-	if (strlen(path) >= max_path_len)
+	int len;
+	if (!(flags & LOCK_NODEREF) && !(path = resolve_symlink(path)))
 		return -1;
+	len = strlen(path) + 5; /* .lock */
+	lk->filename = xmallocz(len);
 	strcpy(lk->filename, path);
-	if (!(flags & LOCK_NODEREF))
-		resolve_symlink(lk->filename, max_path_len);
 	strcat(lk->filename, ".lock");
 	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= lk->fd) {
@@ -153,7 +160,7 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
 				     lk->filename);
 	}
 	else
-		lk->filename[0] = 0;
+		clear_filename(lk);
 	return lk->fd;
 }
 
@@ -231,16 +238,17 @@ int close_lock_file(struct lock_file *lk)
 
 int commit_lock_file(struct lock_file *lk)
 {
-	char result_file[PATH_MAX];
-	size_t i;
-	if (lk->fd >= 0 && close_lock_file(lk))
+	char *result_file;
+	if ((lk->fd >= 0 && close_lock_file(lk)) || !lk->filename)
 		return -1;
-	strcpy(result_file, lk->filename);
-	i = strlen(result_file) - 5; /* .lock */
-	result_file[i] = 0;
-	if (rename(lk->filename, result_file))
+	result_file = xmemdupz(lk->filename,
+			       strlen(lk->filename) - 5 /* .lock */);
+	if (rename(lk->filename, result_file)) {
+		free(result_file);
 		return -1;
-	lk->filename[0] = 0;
+	}
+	free(result_file);
+	clear_filename(lk);
 	return 0;
 }
 
@@ -260,11 +268,11 @@ void set_alternate_index_output(const char *name)
 int commit_locked_index(struct lock_file *lk)
 {
 	if (alternate_index_output) {
-		if (lk->fd >= 0 && close_lock_file(lk))
+		if ((lk->fd >= 0 && close_lock_file(lk)) || !lk->filename)
 			return -1;
 		if (rename(lk->filename, alternate_index_output))
 			return -1;
-		lk->filename[0] = 0;
+		clear_filename(lk);
 		return 0;
 	}
 	else
@@ -273,10 +281,10 @@ int commit_locked_index(struct lock_file *lk)
 
 void rollback_lock_file(struct lock_file *lk)
 {
-	if (lk->filename[0]) {
+	if (lk->filename) {
 		if (lk->fd >= 0)
 			close(lk->fd);
 		unlink_or_warn(lk->filename);
 	}
-	lk->filename[0] = 0;
+	clear_filename(lk);
 }
-- 
1.9.1.346.ga2b5940

[PATCH v2 2/2] Make locked paths absolute when current directory is changed

From: Nguyễn Thái Ngọc Duy <hidden>
Date: 2016-06-15 23:01:59

Locked paths are saved in a linked list so that if something wrong
happens, *.lock are removed. This works fine if we keep cwd the same,
which is true 99% of time except:

 - update-index and read-tree hold the lock on $GIT_DIR/index really
   early, then later on may call setup_work_tree() to move cwd.

 - Suppose a lock is being held (e.g. by "git add") then somewhere
   down the line, somebody calls real_path (e.g. "link_alt_odb_entry"),
   which temporarily moves cwd away and back.

During that time when cwd is moved (either permanently or temporarily)
and we decide to die(), attempts to remove relative *.lock will fail,
and the next operation will complain that some files are still locked.

Avoid this case by turning relative paths to absolute when chdir() is
called (or soon to be called, in setup_git_directory_gently case).

Reported-by: Yue Lin Ho <redacted>
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
 Compared to v1, make_locked_paths_absolute() now always succeeds (ok
 absolute_path could die inside, but that's a separate problem) and
 supports Windows.

 abspath.c     |  2 +-
 cache.h       |  6 ++++++
 git.c         |  6 +++---
 lockfile.c    | 12 ++++++++++++
 path.c        |  4 ++--
 run-command.c |  2 +-
 setup.c       |  3 ++-
 unix-socket.c |  2 +-
 8 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/abspath.c b/abspath.c
index ca33558..78c963f 100644
--- a/abspath.c
+++ b/abspath.c
@@ -87,7 +87,7 @@ static const char *real_path_internal(const char *path, int die_on_error)
 					goto error_out;
 			}
 
-			if (chdir(buf)) {
+			if (chdir_safe(buf)) {
 				if (die_on_error)
 					die_errno("Could not switch to '%s'", buf);
 				else
diff --git a/cache.h b/cache.h
index 9ecb636..48ffa21 100644
--- a/cache.h
+++ b/cache.h
@@ -564,6 +564,12 @@ extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
 extern void update_index_if_able(struct index_state *, struct lock_file *);
+extern void make_locked_paths_absolute(void);
+static inline int chdir_safe(const char *path)
+{
+	make_locked_paths_absolute();
+	return chdir(path);
+}
 
 extern int hold_locked_index(struct lock_file *, int);
 extern int commit_locked_index(struct lock_file *);
diff --git a/git.c b/git.c
index 5b6c761..27766c3 100644
--- a/git.c
+++ b/git.c
@@ -48,7 +48,7 @@ static void save_env(void)
 static void restore_env(void)
 {
 	int i;
-	if (*orig_cwd && chdir(orig_cwd))
+	if (*orig_cwd && chdir_safe(orig_cwd))
 		die_errno("could not move to %s", orig_cwd);
 	for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 		if (orig_env[i])
@@ -206,7 +206,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 				fprintf(stderr, "No directory given for -C.\n" );
 				usage(git_usage_string);
 			}
-			if (chdir((*argv)[1]))
+			if (chdir_safe((*argv)[1]))
 				die_errno("Cannot change to '%s'", (*argv)[1]);
 			if (envchanged)
 				*envchanged = 1;
@@ -292,7 +292,7 @@ static int handle_alias(int *argcp, const char ***argv)
 		ret = 1;
 	}
 
-	if (subdir && chdir(subdir))
+	if (subdir && chdir_safe(subdir))
 		die_errno("Cannot change to '%s'", subdir);
 
 	errno = saved_errno;
diff --git a/lockfile.c b/lockfile.c
index 968b28f..cf1e795 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -288,3 +288,15 @@ void rollback_lock_file(struct lock_file *lk)
 	}
 	clear_filename(lk);
 }
+
+void make_locked_paths_absolute(void)
+{
+	struct lock_file *lk;
+	for (lk = lock_file_list; lk != NULL; lk = lk->next) {
+		if (lk->filename && !is_absolute_path(lk->filename)) {
+			char *to_free = lk->filename;
+			lk->filename = xstrdup(absolute_path(lk->filename));
+			free(to_free);
+		}
+	}
+}
diff --git a/path.c b/path.c
index bc804a3..9e8e101 100644
--- a/path.c
+++ b/path.c
@@ -372,11 +372,11 @@ const char *enter_repo(const char *path, int strict)
 		gitfile = read_gitfile(used_path) ;
 		if (gitfile)
 			strcpy(used_path, gitfile);
-		if (chdir(used_path))
+		if (chdir_safe(used_path))
 			return NULL;
 		path = validated_path;
 	}
-	else if (chdir(path))
+	else if (chdir_safe(path))
 		return NULL;
 
 	if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
diff --git a/run-command.c b/run-command.c
index be07d4a..55f360e 100644
--- a/run-command.c
+++ b/run-command.c
@@ -399,7 +399,7 @@ fail_pipe:
 			close(cmd->out);
 		}
 
-		if (cmd->dir && chdir(cmd->dir))
+		if (cmd->dir && chdir_safe(cmd->dir))
 			die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
 			    cmd->dir);
 		if (cmd->env) {
diff --git a/setup.c b/setup.c
index 0a22f8b..921045e 100644
--- a/setup.c
+++ b/setup.c
@@ -290,7 +290,7 @@ void setup_work_tree(void)
 	git_dir = get_git_dir();
 	if (!is_absolute_path(git_dir))
 		git_dir = real_path(get_git_dir());
-	if (!work_tree || chdir(work_tree))
+	if (!work_tree || chdir_safe(work_tree))
 		die("This operation must be run in a work tree");
 
 	/*
@@ -636,6 +636,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 		die_errno("Unable to read current working directory");
 	offset = len = strlen(cwd);
 
+	make_locked_paths_absolute();
 	/*
 	 * If GIT_DIR is set explicitly, we're not going
 	 * to do any discovery, but we still do repository
diff --git a/unix-socket.c b/unix-socket.c
index 01f119f..eeb8007 100644
--- a/unix-socket.c
+++ b/unix-socket.c
@@ -12,7 +12,7 @@ static int unix_stream_socket(void)
 static int chdir_len(const char *orig, int len)
 {
 	char *path = xmemdupz(orig, len);
-	int r = chdir(path);
+	int r = chdir_safe(path);
 	free(path);
 	return r;
 }
-- 
1.9.1.346.ga2b5940

Re: [PATCH v2 1/2] lockfile.c: remove PATH_MAX limitation (except in resolve_symlink)

From: Philip Oakley <hidden>
Date: 2016-06-15 23:01:59

From: "Nguyễn Thái Ngọc Duy" <redacted>
Something extra is, because struct lock_file is usually used as static
variables in many places. This patch reduces bss section by about 80k
bytes (or 23%) on Linux.
This didn't scan for me. Perhaps it's the punctuation. Maybe:

Additionally, because the struct lock_file variables are [were] in many
places static, this patch reduces the bss section size by about 80k
bytes (or 23%) on Linux.

Does my tweak reflect your intent?
Philip.
quoted hunk
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
This helps remove the length check in v1 of the next patch.

cache.h    |  2 +-
lockfile.c | 56 
++++++++++++++++++++++++++++++++------------------------
2 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/cache.h b/cache.h
index 44aa439..9ecb636 100644
--- a/cache.h
+++ b/cache.h
@@ -554,7 +554,7 @@ struct lock_file {
 int fd;
 pid_t owner;
 char on_list;
- char filename[PATH_MAX];
+ char *filename;
};
#define LOCK_DIE_ON_ERROR 1
#define LOCK_NODEREF 2
diff --git a/lockfile.c b/lockfile.c
index 8fbcb6a..968b28f 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -7,13 +7,19 @@
static struct lock_file *lock_file_list;
static const char *alternate_index_output;

+static void clear_filename(struct lock_file *lk)
+{
+ free(lk->filename);
+ lk->filename = NULL;
+}
+
static void remove_lock_file(void)
{
 pid_t me = getpid();

 while (lock_file_list) {
 if (lock_file_list->owner == me &&
-     lock_file_list->filename[0]) {
+     lock_file_list->filename) {
 if (lock_file_list->fd >= 0)
 close(lock_file_list->fd);
 unlink_or_warn(lock_file_list->filename);
@@ -77,10 +83,16 @@ static char *last_path_elm(char *p)
 * Always returns p.
 */

-static char *resolve_symlink(char *p, size_t s)
+static char *resolve_symlink(const char *in)
{
+ static char p[PATH_MAX];
+ size_t s = sizeof(p);
 int depth = MAXDEPTH;

+ if (strlen(in) >= sizeof(p))
+ return NULL;
+ strcpy(p, in);
+
 while (depth--) {
 char link[PATH_MAX];
 int link_len = readlink(p, link, sizeof(link));
@@ -124,17 +136,12 @@ static char *resolve_symlink(char *p, size_t s)
static int lock_file(struct lock_file *lk, const char *path, int 
flags)
{
- /*
- * subtract 5 from size to make sure there's room for adding
- * ".lock" for the lock file name
- */
- static const size_t max_path_len = sizeof(lk->filename) - 5;
-
- if (strlen(path) >= max_path_len)
+ int len;
+ if (!(flags & LOCK_NODEREF) && !(path = resolve_symlink(path)))
 return -1;
+ len = strlen(path) + 5; /* .lock */
+ lk->filename = xmallocz(len);
 strcpy(lk->filename, path);
- if (!(flags & LOCK_NODEREF))
- resolve_symlink(lk->filename, max_path_len);
 strcat(lk->filename, ".lock");
 lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 if (0 <= lk->fd) {
@@ -153,7 +160,7 @@ static int lock_file(struct lock_file *lk, const 
char *path, int flags)
      lk->filename);
 }
 else
- lk->filename[0] = 0;
+ clear_filename(lk);
 return lk->fd;
}
@@ -231,16 +238,17 @@ int close_lock_file(struct lock_file *lk)
int commit_lock_file(struct lock_file *lk)
{
- char result_file[PATH_MAX];
- size_t i;
- if (lk->fd >= 0 && close_lock_file(lk))
+ char *result_file;
+ if ((lk->fd >= 0 && close_lock_file(lk)) || !lk->filename)
 return -1;
- strcpy(result_file, lk->filename);
- i = strlen(result_file) - 5; /* .lock */
- result_file[i] = 0;
- if (rename(lk->filename, result_file))
+ result_file = xmemdupz(lk->filename,
+        strlen(lk->filename) - 5 /* .lock */);
+ if (rename(lk->filename, result_file)) {
+ free(result_file);
 return -1;
- lk->filename[0] = 0;
+ }
+ free(result_file);
+ clear_filename(lk);
 return 0;
}
@@ -260,11 +268,11 @@ void set_alternate_index_output(const char 
*name)
int commit_locked_index(struct lock_file *lk)
{
 if (alternate_index_output) {
- if (lk->fd >= 0 && close_lock_file(lk))
+ if ((lk->fd >= 0 && close_lock_file(lk)) || !lk->filename)
 return -1;
 if (rename(lk->filename, alternate_index_output))
 return -1;
- lk->filename[0] = 0;
+ clear_filename(lk);
 return 0;
 }
 else
@@ -273,10 +281,10 @@ int commit_locked_index(struct lock_file *lk)
void rollback_lock_file(struct lock_file *lk)
{
- if (lk->filename[0]) {
+ if (lk->filename) {
 if (lk->fd >= 0)
 close(lk->fd);
 unlink_or_warn(lk->filename);
 }
- lk->filename[0] = 0;
+ clear_filename(lk);
}
-- 
1.9.1.346.ga2b5940

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Re: [PATCH v2 1/2] lockfile.c: remove PATH_MAX limitation (except in resolve_symlink)

From: Duy Nguyen <hidden>
Date: 2016-06-15 23:01:59

On Sun, Jul 20, 2014 at 7:47 PM, Philip Oakley [off-list ref] wrote:
From: "Nguyễn Thái Ngọc Duy" <redacted>
quoted
Something extra is, because struct lock_file is usually used as static
variables in many places. This patch reduces bss section by about 80k
bytes (or 23%) on Linux.

This didn't scan for me. Perhaps it's the punctuation. Maybe:

Additionally, because the struct lock_file variables are [were] in many
places static, this patch reduces the bss section size by about 80k

bytes (or 23%) on Linux.

Does my tweak reflect your intent?
Yes. But I should probably put that below the "---" line. We're not
busybox. 80k is pratically nothing.
-- 
Duy

Re: [PATCH v2 2/2] Make locked paths absolute when current directory is changed

From: Ramsay Jones <hidden>
Date: 2016-06-15 23:01:59

On 20/07/14 13:13, Nguyễn Thái Ngọc Duy wrote:
Locked paths are saved in a linked list so that if something wrong
happens, *.lock are removed. This works fine if we keep cwd the same,
which is true 99% of time except:

 - update-index and read-tree hold the lock on $GIT_DIR/index really
   early, then later on may call setup_work_tree() to move cwd.

 - Suppose a lock is being held (e.g. by "git add") then somewhere
   down the line, somebody calls real_path (e.g. "link_alt_odb_entry"),
   which temporarily moves cwd away and back.

During that time when cwd is moved (either permanently or temporarily)
and we decide to die(), attempts to remove relative *.lock will fail,
and the next operation will complain that some files are still locked.

Avoid this case by turning relative paths to absolute when chdir() is
called (or soon to be called, in setup_git_directory_gently case).

Reported-by: Yue Lin Ho <redacted>
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
[snip]
quoted hunk
diff --git a/lockfile.c b/lockfile.c
index 968b28f..cf1e795 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -288,3 +288,15 @@ void rollback_lock_file(struct lock_file *lk)
 	}
 	clear_filename(lk);
 }
+
+void make_locked_paths_absolute(void)
+{
+	struct lock_file *lk;
+	for (lk = lock_file_list; lk != NULL; lk = lk->next) {
+		if (lk->filename && !is_absolute_path(lk->filename)) {
+			char *to_free = lk->filename;
+			lk->filename = xstrdup(absolute_path(lk->filename));
+			free(to_free);
+		}
+	}
+}
I just have to ask, why are we putting relative pathnames in this
list to begin with? Why not use an absolute path when taking the
lock in all cases? (calling absolute_path() and using the result
to take the lock, storing it in the lock_file list, should not be
in the critical path, right? Not that I have measured it, of course! :)

ATB,
Ramsay Jones

Re: [PATCH v2 2/2] Make locked paths absolute when current directory is changed

From: Duy Nguyen <hidden>
Date: 2016-06-15 23:01:59

On Mon, Jul 21, 2014 at 8:27 PM, Ramsay Jones
[off-list ref] wrote:
quoted
+void make_locked_paths_absolute(void)
+{
+     struct lock_file *lk;
+     for (lk = lock_file_list; lk != NULL; lk = lk->next) {
+             if (lk->filename && !is_absolute_path(lk->filename)) {
+                     char *to_free = lk->filename;
+                     lk->filename = xstrdup(absolute_path(lk->filename));
+                     free(to_free);
+             }
+     }
+}
I just have to ask, why are we putting relative pathnames in this
list to begin with? Why not use an absolute path when taking the
lock in all cases? (calling absolute_path() and using the result
to take the lock, storing it in the lock_file list, should not be
in the critical path, right? Not that I have measured it, of course! :)
Conservative :) I'm still scared from 044bbbc (Make git_dir a path
relative to work_tree in setup_work_tree() - 2008-06-19). But yeah
looking through "grep hold_" I think none of the locks is in critical
path. absolute_path() can die() if cwd is longer than PATH_MAX (and
doing this reduces the chances of that happening). But René is adding
strbuf_getcwd() that can remove that PATH_MAX. So I guess we should be
fine with putting absolute_path() in hold_lock_file_...*
-- 
Duy

Re: [PATCH v2 2/2] Make locked paths absolute when current directory is changed

From: Ramsay Jones <hidden>
Date: 2016-06-15 23:01:59

On 21/07/14 14:47, Duy Nguyen wrote:
On Mon, Jul 21, 2014 at 8:27 PM, Ramsay Jones
[off-list ref] wrote:
quoted
quoted
+void make_locked_paths_absolute(void)
+{
+     struct lock_file *lk;
+     for (lk = lock_file_list; lk != NULL; lk = lk->next) {
+             if (lk->filename && !is_absolute_path(lk->filename)) {
+                     char *to_free = lk->filename;
+                     lk->filename = xstrdup(absolute_path(lk->filename));
+                     free(to_free);
+             }
+     }
+}
I just have to ask, why are we putting relative pathnames in this
list to begin with? Why not use an absolute path when taking the
lock in all cases? (calling absolute_path() and using the result
to take the lock, storing it in the lock_file list, should not be
in the critical path, right? Not that I have measured it, of course! :)
Conservative :) I'm still scared from 044bbbc (Make git_dir a path
relative to work_tree in setup_work_tree() - 2008-06-19). But yeah
looking through "grep hold_" I think none of the locks is in critical
path. absolute_path() can die() if cwd is longer than PATH_MAX (and
doing this reduces the chances of that happening). But René is adding
strbuf_getcwd() that can remove that PATH_MAX. So I guess we should be
fine with putting absolute_path() in hold_lock_file_...*
Hmm, yes, thank you for reminding me about 044bbbc. So, yes it could
cause a (small) performance hit and a change in behaviour (die) in
deeply nested working directories. Hmm, OK.

ATB,
Ramsay Jones

[PATCH v3 0/3] Keep .lock file paths absolute

From: Nguyễn Thái Ngọc Duy <hidden>
Date: 2016-06-15 23:02:05

v3 requires rs/strbuf-getcwd, turns paths to absolute from the
beginning, and kills the last use of PATH_MAX in lockfile.c thanks to
strbuf_readlink().

Nguyễn Thái Ngọc Duy (3):
  lockfile.c: remove PATH_MAX limitation (except in resolve_symlink)
  lockfile.c: remove PATH_MAX limit in resolve_symlink()
  lockfile.c: store absolute path

 cache.h                       |  2 +-
 lockfile.c                    | 95 +++++++++++++++++++------------------------
 t/t2107-update-index-basic.sh | 15 +++++++
 3 files changed, 58 insertions(+), 54 deletions(-)

-- 
2.1.0.rc0.78.gc0d8480

[PATCH v3 1/3] lockfile.c: remove PATH_MAX limitation (except in resolve_symlink)

From: Nguyễn Thái Ngọc Duy <hidden>
Date: 2016-06-15 23:02:05

Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
 cache.h    |  2 +-
 lockfile.c | 56 ++++++++++++++++++++++++++++++++------------------------
 2 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/cache.h b/cache.h
index cc46be4..0d8dce7 100644
--- a/cache.h
+++ b/cache.h
@@ -539,7 +539,7 @@ struct lock_file {
 	int fd;
 	pid_t owner;
 	char on_list;
-	char filename[PATH_MAX];
+	char *filename;
 };
 #define LOCK_DIE_ON_ERROR 1
 #define LOCK_NODEREF 2
diff --git a/lockfile.c b/lockfile.c
index 8fbcb6a..968b28f 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -7,13 +7,19 @@
 static struct lock_file *lock_file_list;
 static const char *alternate_index_output;
 
+static void clear_filename(struct lock_file *lk)
+{
+	free(lk->filename);
+	lk->filename = NULL;
+}
+
 static void remove_lock_file(void)
 {
 	pid_t me = getpid();
 
 	while (lock_file_list) {
 		if (lock_file_list->owner == me &&
-		    lock_file_list->filename[0]) {
+		    lock_file_list->filename) {
 			if (lock_file_list->fd >= 0)
 				close(lock_file_list->fd);
 			unlink_or_warn(lock_file_list->filename);
@@ -77,10 +83,16 @@ static char *last_path_elm(char *p)
  * Always returns p.
  */
 
-static char *resolve_symlink(char *p, size_t s)
+static char *resolve_symlink(const char *in)
 {
+	static char p[PATH_MAX];
+	size_t s = sizeof(p);
 	int depth = MAXDEPTH;
 
+	if (strlen(in) >= sizeof(p))
+		return NULL;
+	strcpy(p, in);
+
 	while (depth--) {
 		char link[PATH_MAX];
 		int link_len = readlink(p, link, sizeof(link));
@@ -124,17 +136,12 @@ static char *resolve_symlink(char *p, size_t s)
 
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-	/*
-	 * subtract 5 from size to make sure there's room for adding
-	 * ".lock" for the lock file name
-	 */
-	static const size_t max_path_len = sizeof(lk->filename) - 5;
-
-	if (strlen(path) >= max_path_len)
+	int len;
+	if (!(flags & LOCK_NODEREF) && !(path = resolve_symlink(path)))
 		return -1;
+	len = strlen(path) + 5; /* .lock */
+	lk->filename = xmallocz(len);
 	strcpy(lk->filename, path);
-	if (!(flags & LOCK_NODEREF))
-		resolve_symlink(lk->filename, max_path_len);
 	strcat(lk->filename, ".lock");
 	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= lk->fd) {
@@ -153,7 +160,7 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
 				     lk->filename);
 	}
 	else
-		lk->filename[0] = 0;
+		clear_filename(lk);
 	return lk->fd;
 }
 
@@ -231,16 +238,17 @@ int close_lock_file(struct lock_file *lk)
 
 int commit_lock_file(struct lock_file *lk)
 {
-	char result_file[PATH_MAX];
-	size_t i;
-	if (lk->fd >= 0 && close_lock_file(lk))
+	char *result_file;
+	if ((lk->fd >= 0 && close_lock_file(lk)) || !lk->filename)
 		return -1;
-	strcpy(result_file, lk->filename);
-	i = strlen(result_file) - 5; /* .lock */
-	result_file[i] = 0;
-	if (rename(lk->filename, result_file))
+	result_file = xmemdupz(lk->filename,
+			       strlen(lk->filename) - 5 /* .lock */);
+	if (rename(lk->filename, result_file)) {
+		free(result_file);
 		return -1;
-	lk->filename[0] = 0;
+	}
+	free(result_file);
+	clear_filename(lk);
 	return 0;
 }
 
@@ -260,11 +268,11 @@ void set_alternate_index_output(const char *name)
 int commit_locked_index(struct lock_file *lk)
 {
 	if (alternate_index_output) {
-		if (lk->fd >= 0 && close_lock_file(lk))
+		if ((lk->fd >= 0 && close_lock_file(lk)) || !lk->filename)
 			return -1;
 		if (rename(lk->filename, alternate_index_output))
 			return -1;
-		lk->filename[0] = 0;
+		clear_filename(lk);
 		return 0;
 	}
 	else
@@ -273,10 +281,10 @@ int commit_locked_index(struct lock_file *lk)
 
 void rollback_lock_file(struct lock_file *lk)
 {
-	if (lk->filename[0]) {
+	if (lk->filename) {
 		if (lk->fd >= 0)
 			close(lk->fd);
 		unlink_or_warn(lk->filename);
 	}
-	lk->filename[0] = 0;
+	clear_filename(lk);
 }
-- 
2.1.0.rc0.78.gc0d8480

[PATCH v3 2/3] lockfile.c: remove PATH_MAX limit in resolve_symlink()

From: Nguyễn Thái Ngọc Duy <hidden>
Date: 2016-06-15 23:02:05

Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
 lockfile.c | 47 ++++++++++++++---------------------------------
 1 file changed, 14 insertions(+), 33 deletions(-)
diff --git a/lockfile.c b/lockfile.c
index 968b28f..154915f 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -85,52 +85,33 @@ static char *last_path_elm(char *p)
 
 static char *resolve_symlink(const char *in)
 {
-	static char p[PATH_MAX];
-	size_t s = sizeof(p);
+	static struct strbuf p = STRBUF_INIT;
+	struct strbuf link = STRBUF_INIT;
 	int depth = MAXDEPTH;
 
-	if (strlen(in) >= sizeof(p))
-		return NULL;
-	strcpy(p, in);
+	strbuf_reset(&p);
+	strbuf_addstr(&p, in);
 
 	while (depth--) {
-		char link[PATH_MAX];
-		int link_len = readlink(p, link, sizeof(link));
-		if (link_len < 0) {
-			/* not a symlink anymore */
-			return p;
-		}
-		else if (link_len < sizeof(link))
-			/* readlink() never null-terminates */
-			link[link_len] = '\0';
-		else {
-			warning("%s: symlink too long", p);
-			return p;
-		}
+		if (strbuf_readlink(&link, p.buf, 0) < 0)
+			break;	/* not a symlink anymore */
 
-		if (is_absolute_path(link)) {
+		if (is_absolute_path(link.buf)) {
 			/* absolute path simply replaces p */
-			if (link_len < s)
-				strcpy(p, link);
-			else {
-				warning("%s: symlink too long", p);
-				return p;
-			}
+			strbuf_reset(&p);
+			strbuf_addbuf(&p, &link);
 		} else {
 			/*
 			 * link is a relative path, so I must replace the
 			 * last element of p with it.
 			 */
-			char *r = (char *)last_path_elm(p);
-			if (r - p + link_len < s)
-				strcpy(r, link);
-			else {
-				warning("%s: symlink too long", p);
-				return p;
-			}
+			char *r = (char *)last_path_elm(p.buf);
+			strbuf_setlen(&p, r - p.buf);
+			strbuf_addbuf(&p, &link);
 		}
 	}
-	return p;
+	strbuf_release(&link);
+	return p.buf;
 }
 
 
-- 
2.1.0.rc0.78.gc0d8480

[PATCH v3 3/3] lockfile.c: store absolute path

From: Nguyễn Thái Ngọc Duy <hidden>
Date: 2016-06-15 23:02:05

Locked paths can be saved in a linked list so that if something wrong
happens, *.lock are removed. For relative paths, this works fine if we
keep cwd the same, which is true 99% of time except:

- update-index and read-tree hold the lock on $GIT_DIR/index really
  early, then later on may call setup_work_tree() to move cwd.

- Suppose a lock is being held (e.g. by "git add") then somewhere
  down the line, somebody calls real_path (e.g. "link_alt_odb_entry"),
  which temporarily moves cwd away and back.

During that time when cwd is moved (either permanently or temporarily)
and we decide to die(), attempts to remove relative *.lock will fail,
and the next operation will complain that some files are still locked.

Avoid this case by turning relative paths to absolute before storing
the path in "filename" field.

Reported-by: Yue Lin Ho <redacted>
Helped-by: Ramsay Jones [off-list ref]
Helped-by: Johannes Sixt [off-list ref]
Signed-off-by: Nguyễn Thái Ngọc Duy <redacted>
---
 lockfile.c                    | 10 +++++-----
 t/t2107-update-index-basic.sh | 15 +++++++++++++++
 2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/lockfile.c b/lockfile.c
index 154915f..0aa70a5 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -117,13 +117,13 @@ static char *resolve_symlink(const char *in)
 
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-	int len;
+	struct strbuf sb = STRBUF_INIT;
+
 	if (!(flags & LOCK_NODEREF) && !(path = resolve_symlink(path)))
 		return -1;
-	len = strlen(path) + 5; /* .lock */
-	lk->filename = xmallocz(len);
-	strcpy(lk->filename, path);
-	strcat(lk->filename, ".lock");
+	strbuf_add_absolute_path(&sb, path);
+	strbuf_addstr(&sb, ".lock");
+	lk->filename = strbuf_detach(&sb, NULL);
 	lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= lk->fd) {
 		if (!lock_file_list) {
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
index 1bafb90..dfe02f4 100755
--- a/t/t2107-update-index-basic.sh
+++ b/t/t2107-update-index-basic.sh
@@ -65,4 +65,19 @@ test_expect_success '--cacheinfo mode,sha1,path (new syntax)' '
 	test_cmp expect actual
 '
 
+test_expect_success '.lock files cleaned up' '
+	mkdir cleanup &&
+	(
+	cd cleanup &&
+	mkdir worktree &&
+	git init repo &&
+	cd repo &&
+	git config core.worktree ../../worktree &&
+	# --refresh triggers late setup_work_tree,
+	# active_cache_changed is zero, rollback_lock_file fails
+	git update-index --refresh &&
+	! test -f .git/index.lock
+	)
+'
+
 test_done
-- 
2.1.0.rc0.78.gc0d8480
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help