git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC

From
Junio C Hamano <gitster@pobox.com>
Date
Oct 28, 2016, 01:08 UTC
Message-ID
<xmqqa8dp46wx.fsf@gitster.mtv.corp.google.com>
In-Reply-To
<CA+55aFw83E+zOd+z5h-CA-3NhrLjVr-anL6pubrSWttYx3zu8g@mail.gmail.com>
Linus Torvalds <torvalds@linux-foundation.org> writes:
Show 8 quoted lines
> On Thu, Oct 27, 2016 at 4:36 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Would the best endgame shape for this function be to open with
>> O_NOATIME (and retry without), and then add CLOEXEC with fcntl(2)
>> but ignoring an error from it, I guess?  That would be the closest
>> to what we historically had, I would think.
>
> I think that's the best model.
OK, so perhaps like this.
-- >8 --
Subject: git_open(): untangle possible NOATIME and CLOEXEC interactions

The way we structured the fallback-retry for opening with O_NOATIME and O_CLOEXEC meant that if we failed due to lack of support to open the file with O_NOATIME option (i.e. EINVAL), we would still try to drop O_CLOEXEC first and retry, and then drop O_NOATIME. A platform on which O_NOATIME is defined in the header without support from the kernel wouldn't have a chance to open with O_CLOEXEC option due to this code structure.

Arguably, O_CLOEXEC is more important than O_NOATIME, as the latter is mostly about performance, while the former can affect correctness. Let's revert the recent changes to the way git_open() attempts to open a file with O_NOATIME and retries without to the original sequence, and then use a separate fcntl(fd, F_SETFD, FD_CLOEXEC) on the resulting file descriptor. The helper to do the latter can be usable in the codepath in ce_compare_data() that was recently added to open a file descriptor with O_CLOEXEC, so let's refactor that codepath with the helper while we are at it.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 git-compat-util.h |  5 +++--
 read-cache.c      | 12 ++++--------
 sha1_file.c       | 49 ++++++++++++++++++++++++++++++-------------------
 3 files changed, 37 insertions(+), 29 deletions(-)
diff --git a/git-compat-util.h b/git-compat-util.h
index 43718dabae..a751630db5 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -679,9 +679,10 @@ char *gitstrdup(const char *s);
 #define getpagesize() sysconf(_SC_PAGESIZE)
 #endif
 
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 0
 #endif
+extern int git_set_cloexec(int);
 
 #ifdef FREAD_READS_DIRECTORIES
 #ifdef fopen
diff --git a/read-cache.c b/read-cache.c
index db5d910642..fb91514885 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -156,17 +156,13 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
 {
 	int match = -1;
-	static int cloexec = O_CLOEXEC;
-	int fd = open(ce->name, O_RDONLY | cloexec);
-
-	if ((cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
-		/* Try again w/o O_CLOEXEC: the kernel might not support it */
-		cloexec &= ~O_CLOEXEC;
-		fd = open(ce->name, O_RDONLY | cloexec);
-	}
+	int fd = open(ce->name, O_RDONLY);
 
 	if (fd >= 0) {
 		unsigned char sha1[20];
+
+		/* do not let child processes to hold onto the open fd */
+		git_set_cloexec(fd);
 		if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
 			match = hashcmp(sha1, ce->oid.hash);
 		/* index_fd() closed the file descriptor already */
diff --git a/sha1_file.c b/sha1_file.c
index 09045df1dc..41383a6c20 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1559,31 +1559,42 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
 	return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
-int git_open(const char *name)
+int git_set_cloexec(int fd)
 {
-	static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
+	static int cloexec = FD_CLOEXEC;
 
-	for (;;) {
-		int fd;
+	if (cloexec) {
+		if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
+			cloexec = 0;
+		/*
+		 * We might want to diagnose and complain upon seeing
+		 * an error from this call, but let's keep the same
+		 * behaviour as before for now.
+		 */
+	}
+	return 0;
+}
 
-		errno = 0;
-		fd = open(name, O_RDONLY | sha1_file_open_flag);
-		if (fd >= 0)
-			return fd;
+int git_open(const char *name)
+{
+	static int noatime = O_NOATIME;
+	int fd;
 
-		/* Try again w/o O_CLOEXEC: the kernel might not support it */
-		if ((sha1_file_open_flag & O_CLOEXEC) && errno == EINVAL) {
-			sha1_file_open_flag &= ~O_CLOEXEC;
-			continue;
-		}
+	errno = 0;
+	fd = open(name, O_RDONLY | noatime);
 
-		/* Might the failure be due to O_NOATIME? */
-		if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) {
-			sha1_file_open_flag &= ~O_NOATIME;
-			continue;
-		}
-		return -1;
+	/* Might the failure be due to O_NOATIME? */
+	if ((noatime & O_NOATIME) && errno != ENOENT) {
+		noatime = 0;
+		fd = open(name, O_RDONLY);
 	}
+
+	if (fd < 0)
+		return fd;
+
+	/* do not let child processes to hold onto the open fd */
+	git_set_cloexec(fd);
+	return fd;
 }
 
 static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
Previous: Linus TorvaldsNext: Junio C Hamano
Message 31 of 54 in “Use CLOEXEC to avoid fd leaks”
  1. 0/2 Use CLOEXEC to avoid fd leakslarsxschneider@gmail.com, Oct 24, 2016
  2. 1/2 sha1_file: open window into packfiles with CLOEXEClarsxschneider@gmail.com, Oct 24, 2016
  3. Johannes SchindelinOct 25, 2016
  4. Junio C HamanoOct 25, 2016
  5. 2/2 read-cache: make sure file handles are not inherited by child processeslarsxschneider@gmail.com, Oct 24, 2016
  6. Eric WongOct 24, 2016
  7. Junio C HamanoOct 24, 2016
  8. Johannes SchindelinOct 25, 2016
  9. Junio C HamanoOct 25, 2016
  10. Johannes SixtOct 24, 2016
  11. Lars SchneiderOct 24, 2016
  12. Johannes SixtOct 25, 2016
  13. Junio C HamanoOct 24, 2016
  14. Johannes SchindelinOct 25, 2016
  15. 0/3 quick reroll of Lars's git_open() w/ O_CLOEXECJunio C Hamano, Oct 25, 2016
  16. 2/3 sha1_file: open window into packfiles with O_CLOEXECJunio C Hamano, Oct 25, 2016
  17. Jeff KingOct 26, 2016
  18. Junio C HamanoOct 26, 2016
  19. Jeff KingOct 26, 2016
  20. Junio C HamanoOct 26, 2016
  21. Jeff KingOct 26, 2016
  22. Junio C HamanoOct 26, 2016
  23. Jeff KingOct 27, 2016
  24. Junio C HamanoOct 27, 2016
  25. Linus TorvaldsOct 27, 2016
  26. Junio C HamanoOct 27, 2016
  27. Linus TorvaldsOct 27, 2016
  28. Linus TorvaldsOct 27, 2016
  29. Junio C HamanoOct 27, 2016
  30. Linus TorvaldsOct 27, 2016
  31. Junio C HamanoOct 28, 2016
  32. Junio C HamanoOct 28, 2016
  33. Eric WongOct 28, 2016
  34. Johannes SchindelinOct 28, 2016
  35. Linus TorvaldsOct 28, 2016
  36. Junio C HamanoOct 28, 2016
  37. Linus TorvaldsOct 28, 2016
  38. Junio C HamanoOct 28, 2016
  39. Junio C HamanoOct 29, 2016
  40. Johannes SchindelinOct 29, 2016
  41. Linus TorvaldsOct 29, 2016
  42. Junio C HamanoOct 31, 2016
  43. Jeff KingOct 31, 2016
  44. Junio C HamanoOct 31, 2016
  45. Jeff KingOct 31, 2016
  46. Junio C HamanoOct 28, 2016
  47. Junio C HamanoOct 28, 2016
  48. Jeff KingOct 28, 2016
  49. 1/3 sha1_file: rename git_open_noatime() to git_open()Junio C Hamano, Oct 25, 2016
  50. 3/3 read-cache: make sure file handles are not inherited by child processesJunio C Hamano, Oct 25, 2016
  51. Eric WongOct 25, 2016
  52. Junio C HamanoOct 25, 2016
  53. Lars SchneiderOct 25, 2016
  54. Junio C HamanoOct 25, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.