All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks
@ 2016-10-24 18:02 larsxschneider
  2016-10-24 18:02 ` [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC larsxschneider
                   ` (3 more replies)
  0 siblings, 4 replies; 54+ messages in thread
From: larsxschneider @ 2016-10-24 18:02 UTC (permalink / raw)
  To: git; +Cc: Johannes.Schindelin, e, jnareb, gitster, Lars Schneider

From: Lars Schneider <larsxschneider@gmail.com>

Use the CLOEXEC flag similar to 05d1ed61 to fix leaked file descriptors.

This mini patch series is necessary to make the "ls/filter-process" topic
work properly for Windows. Right now the topic generates test failures
on Windows as reported by Dscho:
http://public-inbox.org/git/alpine.DEB.2.20.1610211457030.3264@virtualbox/

Patch 1/2 was contemplated by Junio here:
http://public-inbox.org/git/xmqq37lnbbpk.fsf@gitster.mtv.corp.google.com/

Thanks to
    Eric, Jakub, Dscho, and Junio for the review of v1,
Lars



## Changes since v1
 * add fallbacks in case O_CLOEXEC is not available
 * rename 'git_open_noatime_cloexec' to 'git_open' (Eric, Dscho)
 * rebased the topic on `next` to fix a merge conflict


Lars Schneider (2):
  sha1_file: open window into packfiles with CLOEXEC
  read-cache: make sure file handles are not inherited by child
    processes

 builtin/pack-objects.c |  2 +-
 cache.h                |  2 +-
 pack-bitmap.c          |  2 +-
 read-cache.c           |  6 +++++-
 sha1_file.c            | 26 ++++++++++++++++----------
 5 files changed, 24 insertions(+), 14 deletions(-)



## Interdiff (v1..v2)

diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 0fdd331..0fd52bd 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -720,7 +720,7 @@ static off_t write_reused_pack(struct sha1file *f)
 	if (!is_pack_valid(reuse_packfile))
 		die("packfile is invalid: %s", reuse_packfile->pack_name);

-	fd = git_open_noatime_cloexec(reuse_packfile->pack_name);
+	fd = git_open(reuse_packfile->pack_name);
 	if (fd < 0)
 		die_errno("unable to open packfile for reuse: %s",
 			  reuse_packfile->pack_name);
diff --git a/cache.h b/cache.h
index 0dea548..419b7a0 100644
--- a/cache.h
+++ b/cache.h
@@ -1125,7 +1125,7 @@ extern int write_sha1_file(const void *buf, unsigned long len, const char *type,
 extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
-extern int git_open_noatime_cloexec(const char *name);
+extern int git_open(const char *name);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 1b39e5d..39bcc16 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -266,7 +266,7 @@ static int open_pack_bitmap_1(struct packed_git *packfile)
 		return -1;

 	idx_name = pack_bitmap_filename(packfile);
-	fd = git_open_noatime_cloexec(idx_name);
+	fd = git_open(idx_name);
 	free(idx_name);

 	if (fd < 0)
diff --git a/read-cache.c b/read-cache.c
index 200d4fa..b594865 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -158,6 +158,10 @@ static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
 	int match = -1;
 	int fd = open(ce->name, O_RDONLY | O_CLOEXEC);

+	if (O_CLOEXEC && fd < 0 && errno == EINVAL)
+		/* Try again w/o O_CLOEXEC: the kernel might not support it */
+		fd = open(ce->name, O_RDONLY);
+
 	if (fd >= 0) {
 		unsigned char sha1[20];
 		if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
diff --git a/sha1_file.c b/sha1_file.c
index dbe027b..93b836b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -370,7 +370,7 @@ void read_info_alternates(const char * relative_base, int depth)
 	int fd;

 	path = xstrfmt("%s/info/alternates", relative_base);
-	fd = git_open_noatime_cloexec(path);
+	fd = git_open(path);
 	free(path);
 	if (fd < 0)
 		return;
@@ -663,7 +663,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
 	struct pack_idx_header *hdr;
 	size_t idx_size;
 	uint32_t version, nr, i, *index;
-	int fd = git_open_noatime_cloexec(path);
+	int fd = git_open(path);
 	struct stat st;

 	if (fd < 0)
@@ -1069,7 +1069,7 @@ static int open_packed_git_1(struct packed_git *p)
 	while (pack_max_fds <= pack_open_fds && close_one_pack())
 		; /* nothing */

-	p->pack_fd = git_open_noatime_cloexec(p->pack_name);
+	p->pack_fd = git_open(p->pack_name);
 	if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
 		return -1;
 	pack_open_fds++;
@@ -1586,7 +1586,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
 	return hashcmp(sha1, real_sha1) ? -1 : 0;
 }

-int git_open_noatime_cloexec(const char *name)
+int git_open(const char *name)
 {
 	static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;

@@ -1598,12 +1598,18 @@ int git_open_noatime_cloexec(const char *name)
 		if (fd >= 0)
 			return fd;

-		/* Might the failure be due to O_NOATIME? */
-		if (errno != ENOENT && sha1_file_open_flag) {
-			sha1_file_open_flag = 0;
+		/* Try again w/o O_CLOEXEC: the kernel might not support it */
+		if (O_CLOEXEC && errno == EINVAL &&
+			(sha1_file_open_flag & O_CLOEXEC)) {
+			sha1_file_open_flag &= ~O_CLOEXEC;
 			continue;
 		}

+		/* 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;
 	}
 }
@@ -1632,7 +1638,7 @@ static int open_sha1_file(const unsigned char *sha1)
 	struct alternate_object_database *alt;
 	int most_interesting_errno;

-	fd = git_open_noatime_cloexec(sha1_file_name(sha1));
+	fd = git_open(sha1_file_name(sha1));
 	if (fd >= 0)
 		return fd;
 	most_interesting_errno = errno;
@@ -1640,7 +1646,7 @@ static int open_sha1_file(const unsigned char *sha1)
 	prepare_alt_odb();
 	for (alt = alt_odb_list; alt; alt = alt->next) {
 		const char *path = alt_sha1_path(alt, sha1);
-		fd = git_open_noatime_cloexec(path);
+		fd = git_open(path);
 		if (fd >= 0)
 			return fd;
 		if (most_interesting_errno == ENOENT)

--
2.10.0


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC
  2016-10-24 18:02 [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks larsxschneider
@ 2016-10-24 18:02 ` larsxschneider
  2016-10-25 10:27   ` Johannes Schindelin
  2016-10-24 18:03 ` [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes larsxschneider
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 54+ messages in thread
From: larsxschneider @ 2016-10-24 18:02 UTC (permalink / raw)
  To: git; +Cc: Johannes.Schindelin, e, jnareb, gitster, Lars Schneider

From: Lars Schneider <larsxschneider@gmail.com>

All processes that the Git main process spawns inherit the open file
descriptors of the main process. These leaked file descriptors can
cause problems.

Use the CLOEXEC flag similar to 05d1ed61 to fix the leaked file
descriptors. Since `git_open_noatime` does not describe the function
properly anymore rename it to `git_open`.

Signed-off-by: Lars Schneider <larsxschneider@gmail.com>
---
 builtin/pack-objects.c |  2 +-
 cache.h                |  2 +-
 pack-bitmap.c          |  2 +-
 sha1_file.c            | 26 ++++++++++++++++----------
 4 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 1e7c2a9..0fd52bd 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -720,7 +720,7 @@ static off_t write_reused_pack(struct sha1file *f)
 	if (!is_pack_valid(reuse_packfile))
 		die("packfile is invalid: %s", reuse_packfile->pack_name);
 
-	fd = git_open_noatime(reuse_packfile->pack_name);
+	fd = git_open(reuse_packfile->pack_name);
 	if (fd < 0)
 		die_errno("unable to open packfile for reuse: %s",
 			  reuse_packfile->pack_name);
diff --git a/cache.h b/cache.h
index b7f34b4..419b7a0 100644
--- a/cache.h
+++ b/cache.h
@@ -1125,7 +1125,7 @@ extern int write_sha1_file(const void *buf, unsigned long len, const char *type,
 extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
-extern int git_open_noatime(const char *name);
+extern int git_open(const char *name);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index b949e51..39bcc16 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -266,7 +266,7 @@ static int open_pack_bitmap_1(struct packed_git *packfile)
 		return -1;
 
 	idx_name = pack_bitmap_filename(packfile);
-	fd = git_open_noatime(idx_name);
+	fd = git_open(idx_name);
 	free(idx_name);
 
 	if (fd < 0)
diff --git a/sha1_file.c b/sha1_file.c
index 1e41954..93b836b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -370,7 +370,7 @@ void read_info_alternates(const char * relative_base, int depth)
 	int fd;
 
 	path = xstrfmt("%s/info/alternates", relative_base);
-	fd = git_open_noatime(path);
+	fd = git_open(path);
 	free(path);
 	if (fd < 0)
 		return;
@@ -663,7 +663,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
 	struct pack_idx_header *hdr;
 	size_t idx_size;
 	uint32_t version, nr, i, *index;
-	int fd = git_open_noatime(path);
+	int fd = git_open(path);
 	struct stat st;
 
 	if (fd < 0)
@@ -1069,7 +1069,7 @@ static int open_packed_git_1(struct packed_git *p)
 	while (pack_max_fds <= pack_open_fds && close_one_pack())
 		; /* nothing */
 
-	p->pack_fd = git_open_noatime(p->pack_name);
+	p->pack_fd = git_open(p->pack_name);
 	if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
 		return -1;
 	pack_open_fds++;
@@ -1586,9 +1586,9 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
 	return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
-int git_open_noatime(const char *name)
+int git_open(const char *name)
 {
-	static int sha1_file_open_flag = O_NOATIME;
+	static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
 
 	for (;;) {
 		int fd;
@@ -1598,12 +1598,18 @@ int git_open_noatime(const char *name)
 		if (fd >= 0)
 			return fd;
 
-		/* Might the failure be due to O_NOATIME? */
-		if (errno != ENOENT && sha1_file_open_flag) {
-			sha1_file_open_flag = 0;
+		/* Try again w/o O_CLOEXEC: the kernel might not support it */
+		if (O_CLOEXEC && errno == EINVAL &&
+			(sha1_file_open_flag & O_CLOEXEC)) {
+			sha1_file_open_flag &= ~O_CLOEXEC;
 			continue;
 		}
 
+		/* 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;
 	}
 }
@@ -1632,7 +1638,7 @@ static int open_sha1_file(const unsigned char *sha1)
 	struct alternate_object_database *alt;
 	int most_interesting_errno;
 
-	fd = git_open_noatime(sha1_file_name(sha1));
+	fd = git_open(sha1_file_name(sha1));
 	if (fd >= 0)
 		return fd;
 	most_interesting_errno = errno;
@@ -1640,7 +1646,7 @@ static int open_sha1_file(const unsigned char *sha1)
 	prepare_alt_odb();
 	for (alt = alt_odb_list; alt; alt = alt->next) {
 		const char *path = alt_sha1_path(alt, sha1);
-		fd = git_open_noatime(path);
+		fd = git_open(path);
 		if (fd >= 0)
 			return fd;
 		if (most_interesting_errno == ENOENT)
-- 
2.10.0


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-24 18:02 [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks larsxschneider
  2016-10-24 18:02 ` [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC larsxschneider
@ 2016-10-24 18:03 ` larsxschneider
  2016-10-24 18:39   ` Eric Wong
  2016-10-24 19:22   ` Johannes Sixt
  2016-10-24 18:23 ` [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks Junio C Hamano
  2016-10-25 11:27 ` Johannes Schindelin
  3 siblings, 2 replies; 54+ messages in thread
From: larsxschneider @ 2016-10-24 18:03 UTC (permalink / raw)
  To: git; +Cc: Johannes.Schindelin, e, jnareb, gitster, Lars Schneider

From: Lars Schneider <larsxschneider@gmail.com>

This fixes "convert: add filter.<driver>.process option" (edcc8581) on
Windows.

Consider the case of a file that requires filtering and is present in
branch A but not in branch B. If A is the current HEAD and we checkout B
then the following happens:

1. ce_compare_data() opens the file
2.   index_fd() detects that the file requires to run a clean filter and
     calls index_stream_convert_blob()
4.     index_stream_convert_blob() calls convert_to_git_filter_fd()
5.       convert_to_git_filter_fd() calls apply_filter() which creates a
         new long running filter process (in case it is the first file
         of this kind to be filtered)
6.       The new filter process inherits all file handles. This is the
         default on Linux/OSX and is explicitly defined in the
         `CreateProcessW` call in `mingw.c` on Windows.
7. ce_compare_data() closes the file
8. Git unlinks the file as it is not present in B

The unlink operation does not work on Windows because the filter process
has still an open handle to the file. On Linux/OSX the unlink operation
succeeds but the file descriptors still leak into the child process.

Fix this problem by opening files in read-cache with the CLOEXEC flag to
ensure that the file descriptor does not remain open in a newly spawned
process similar to 05d1ed61.

Signed-off-by: Lars Schneider <larsxschneider@gmail.com>
---
 read-cache.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/read-cache.c b/read-cache.c
index 38d67fa..b594865 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -156,7 +156,11 @@ 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;
-	int fd = open(ce->name, O_RDONLY);
+	int fd = open(ce->name, O_RDONLY | O_CLOEXEC);
+
+	if (O_CLOEXEC && fd < 0 && errno == EINVAL)
+		/* Try again w/o O_CLOEXEC: the kernel might not support it */
+		fd = open(ce->name, O_RDONLY);
 
 	if (fd >= 0) {
 		unsigned char sha1[20];
-- 
2.10.0


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks
  2016-10-24 18:02 [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks larsxschneider
  2016-10-24 18:02 ` [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC larsxschneider
  2016-10-24 18:03 ` [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes larsxschneider
@ 2016-10-24 18:23 ` Junio C Hamano
  2016-10-25 11:27 ` Johannes Schindelin
  3 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-24 18:23 UTC (permalink / raw)
  To: larsxschneider; +Cc: git, Johannes.Schindelin, e, jnareb

larsxschneider@gmail.com writes:

> ## Changes since v1
>  * add fallbacks in case O_CLOEXEC is not available

That is a good idea.

>  * rename 'git_open_noatime_cloexec' to 'git_open' (Eric, Dscho)

OK.  This is the old git_open_noatime() that is meant to be used
ONLY for the files Git uses for its internal implementation, and
never for end-user files.  I think it is a good idea to open them
with O_CLOEXEC.

And the separate patch to use O_CLOEXEC in ce_compare_data() that
opens a working tree file for reading does not use git_open(), which
is also correct.  I like it.

>  * rebased the topic on `next` to fix a merge conflict

I think this applies cleanly to 'master', so that is where I'd fork
my copy at.

Thanks.


^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-24 18:03 ` [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes larsxschneider
@ 2016-10-24 18:39   ` Eric Wong
  2016-10-24 19:53     ` Junio C Hamano
  2016-10-24 19:22   ` Johannes Sixt
  1 sibling, 1 reply; 54+ messages in thread
From: Eric Wong @ 2016-10-24 18:39 UTC (permalink / raw)
  To: larsxschneider; +Cc: git, Johannes.Schindelin, jnareb, gitster

larsxschneider@gmail.com wrote:
> +++ b/read-cache.c
> @@ -156,7 +156,11 @@ 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;
> -	int fd = open(ce->name, O_RDONLY);
> +	int fd = open(ce->name, O_RDONLY | O_CLOEXEC);
> +
> +	if (O_CLOEXEC && fd < 0 && errno == EINVAL)
> +		/* Try again w/o O_CLOEXEC: the kernel might not support it */
> +		fd = open(ce->name, O_RDONLY);

In the case of O_CLOEXEC != 0 and repeated EINVALs,
it'd be good to use something like sha1_file_open_flag as in 1/2
so we don't repeatedly hit EINVAL.  Thanks.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-24 18:03 ` [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes larsxschneider
  2016-10-24 18:39   ` Eric Wong
@ 2016-10-24 19:22   ` Johannes Sixt
  2016-10-24 19:53     ` Lars Schneider
  1 sibling, 1 reply; 54+ messages in thread
From: Johannes Sixt @ 2016-10-24 19:22 UTC (permalink / raw)
  To: larsxschneider; +Cc: git, Johannes.Schindelin, e, jnareb, gitster

Am 24.10.2016 um 20:03 schrieb larsxschneider@gmail.com:
> From: Lars Schneider <larsxschneider@gmail.com>
>
> This fixes "convert: add filter.<driver>.process option" (edcc8581) on
> Windows.

Today's next falls flat on its face on Windows in t0021.15 "required 
process filter should filter data"; might it be the failure meant here? 
(I haven't dug deeper, yet.)

++ test_config_global filter.protocol.process 
'/d/Src/mingw-git/t/t0021/rot13-filter.pl clean smudge'
++ test_when_finished 'test_unconfig --global 
'\''filter.protocol.process'\'''
++ test 0 = 0
++ test_cleanup='{ test_unconfig --global '\''filter.protocol.process'\''
                 } && (exit "$eval_ret"); eval_ret=$?; :'
++ git config --global filter.protocol.process 
'/d/Src/mingw-git/t/t0021/rot13-filter.pl clean smudge'
++ test_config_global filter.protocol.required true
++ test_when_finished 'test_unconfig --global 
'\''filter.protocol.required'\'''
++ test 0 = 0
++ test_cleanup='{ test_unconfig --global '\''filter.protocol.required'\''
                 } && (exit "$eval_ret"); eval_ret=$?; { test_unconfig 
--global '\''filter.protocol.process'\''
                 } && (exit "$eval_ret"); eval_ret=$?; :'
++ git config --global filter.protocol.required true
++ rm -rf repo
++ mkdir repo
++ cd repo
++ git init
Initialized empty Git repository in d:/Src/mingw-git/t/trash 
directory.t0021-conversion/repo/.git/
++ echo git-stderr.log
++ echo '*.r filter=protocol'
++ git add .
++ git commit . -m 'test commit 1'
[master (root-commit) aa5dd37] test commit 1
  Author: A U Thor <author@example.com>
  2 files changed, 2 insertions(+)
  create mode 100644 .gitattributes
  create mode 100644 .gitignore
++ git branch empty-branch
++ cp 'd:/Src/mingw-git/t/trash directory.t0021-conversion/test.o' test.r
++ cp 'd:/Src/mingw-git/t/trash directory.t0021-conversion/test2.o' test2.r
++ mkdir testsubdir
++ cp 'd:/Src/mingw-git/t/trash directory.t0021-conversion/test3 
'\''sq'\'',$x.o' 'testsubdir/test3 '\''sq'\'',$x.r'
+++ file_size test.r
+++ cat test.r
+++ wc -c
+++ sed 's/^[ ]*//'
++ S=57
+++ file_size test2.r
+++ cat test2.r
+++ wc -c
+++ sed 's/^[ ]*//'
++ S2=14
+++ file_size 'testsubdir/test3 '\''sq'\'',$x.r'
+++ cat 'testsubdir/test3 '\''sq'\'',$x.r'
+++ wc -c
+++ sed 's/^[ ]*//'
++ S3=49
++ filter_git add .
++ rm -f rot13-filter.log
++ git add .
error: last command exited with $?=128
not ok 15 - required process filter should filter data


^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-24 18:39   ` Eric Wong
@ 2016-10-24 19:53     ` Junio C Hamano
  2016-10-25 10:33       ` Johannes Schindelin
  0 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-24 19:53 UTC (permalink / raw)
  To: Eric Wong; +Cc: larsxschneider, git, Johannes.Schindelin, jnareb

Eric Wong <e@80x24.org> writes:

> larsxschneider@gmail.com wrote:
>> +++ b/read-cache.c
>> @@ -156,7 +156,11 @@ 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;
>> -	int fd = open(ce->name, O_RDONLY);
>> +	int fd = open(ce->name, O_RDONLY | O_CLOEXEC);
>> +
>> +	if (O_CLOEXEC && fd < 0 && errno == EINVAL)
>> +		/* Try again w/o O_CLOEXEC: the kernel might not support it */
>> +		fd = open(ce->name, O_RDONLY);
>
> In the case of O_CLOEXEC != 0 and repeated EINVALs,
> it'd be good to use something like sha1_file_open_flag as in 1/2
> so we don't repeatedly hit EINVAL.  Thanks.

Sounds sane.  

It's just only once, so perhaps we do not mind a recursion like
this?

 read-cache.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/read-cache.c b/read-cache.c
index b594865d89..a6978b9321 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -156,11 +156,14 @@ 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;
-	int fd = open(ce->name, O_RDONLY | O_CLOEXEC);
+	static int cloexec = O_CLOEXEC;
+	int fd = open(ce->name, O_RDONLY | cloexec);
 
-	if (O_CLOEXEC && fd < 0 && errno == EINVAL)
+	if ((cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
 		/* Try again w/o O_CLOEXEC: the kernel might not support it */
-		fd = open(ce->name, O_RDONLY);
+		cloexec &= ~O_CLOEXEC;
+		return ce_compare_data(ce, st);
+	}
 
 	if (fd >= 0) {
 		unsigned char sha1[20];

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-24 19:22   ` Johannes Sixt
@ 2016-10-24 19:53     ` Lars Schneider
  2016-10-25 21:39       ` Johannes Sixt
  0 siblings, 1 reply; 54+ messages in thread
From: Lars Schneider @ 2016-10-24 19:53 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Johannes.Schindelin, e, jnareb, gitster


> On 24 Oct 2016, at 21:22, Johannes Sixt <j6t@kdbg.org> wrote:
> 
> Am 24.10.2016 um 20:03 schrieb larsxschneider@gmail.com:
>> From: Lars Schneider <larsxschneider@gmail.com>
>> 
>> This fixes "convert: add filter.<driver>.process option" (edcc8581) on
>> Windows.
> 
> Today's next falls flat on its face on Windows in t0021.15 "required process filter should filter data"; might it be the failure meant here? (I haven't dug deeper, yet.)

Yes, this is the failure meant here :-)

Cheers,
Lars


^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC
  2016-10-24 18:02 ` [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC larsxschneider
@ 2016-10-25 10:27   ` Johannes Schindelin
  2016-10-25 16:58     ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Johannes Schindelin @ 2016-10-25 10:27 UTC (permalink / raw)
  To: Lars Schneider; +Cc: git, e, jnareb, gitster

Hi Lars,

On Mon, 24 Oct 2016, larsxschneider@gmail.com wrote:

> From: Lars Schneider <larsxschneider@gmail.com>
> 
> All processes that the Git main process spawns inherit the open file
> descriptors of the main process. These leaked file descriptors can
> cause problems.
> 
> Use the CLOEXEC flag similar to 05d1ed61 to fix the leaked file
> descriptors. Since `git_open_noatime` does not describe the function
> properly anymore rename it to `git_open`.

The patch series may be a little bit more pleasant to read if you renamed
git_open_noatime() to git_open() first, in a separate commit.

> @@ -1598,12 +1598,18 @@ int git_open_noatime(const char *name)
>  		if (fd >= 0)
>  			return fd;
>  
> -		/* Might the failure be due to O_NOATIME? */
> -		if (errno != ENOENT && sha1_file_open_flag) {
> -			sha1_file_open_flag = 0;
> +		/* Try again w/o O_CLOEXEC: the kernel might not support it */
> +		if (O_CLOEXEC && errno == EINVAL &&
> +			(sha1_file_open_flag & O_CLOEXEC)) {
> +			sha1_file_open_flag &= ~O_CLOEXEC;

How about

		if ((O_CLOEXEC & sha1_file_open_flag) && errno == EINVAL) {
			sha1_file_open_flag &= ~O_CLOEXEC;

instead? It is shorter and should be just as easily optimized out by a
C compiler if O_CLOEXEC was defined as 0.

>  			continue;
>  		}
>  
> +		/* Might the failure be due to O_NOATIME? */
> +		if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) {
> +			sha1_file_open_flag &= ~O_NOATIME;
> +			continue;
> +		}

I *think* the --patience diff option would have made that patch a little
more obvious.

Otherwise, the patch looks fine to me,
Dscho

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-24 19:53     ` Junio C Hamano
@ 2016-10-25 10:33       ` Johannes Schindelin
  2016-10-25 17:02         ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Johannes Schindelin @ 2016-10-25 10:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Eric Wong, larsxschneider, git, jnareb

Hi Junio,

On Mon, 24 Oct 2016, Junio C Hamano wrote:

> Eric Wong <e@80x24.org> writes:
> 
> > larsxschneider@gmail.com wrote:
> >> +++ b/read-cache.c
> >> @@ -156,7 +156,11 @@ 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;
> >> -	int fd = open(ce->name, O_RDONLY);
> >> +	int fd = open(ce->name, O_RDONLY | O_CLOEXEC);
> >> +
> >> +	if (O_CLOEXEC && fd < 0 && errno == EINVAL)
> >> +		/* Try again w/o O_CLOEXEC: the kernel might not support it */
> >> +		fd = open(ce->name, O_RDONLY);
> >
> > In the case of O_CLOEXEC != 0 and repeated EINVALs,
> > it'd be good to use something like sha1_file_open_flag as in 1/2
> > so we don't repeatedly hit EINVAL.  Thanks.
> 
> Sounds sane.  
> 
> It's just only once, so perhaps we do not mind a recursion like
> this?
> 
>  read-cache.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/read-cache.c b/read-cache.c
> index b594865d89..a6978b9321 100644
> --- a/read-cache.c
> +++ b/read-cache.c
> @@ -156,11 +156,14 @@ 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;
> -	int fd = open(ce->name, O_RDONLY | O_CLOEXEC);
> +	static int cloexec = O_CLOEXEC;
> +	int fd = open(ce->name, O_RDONLY | cloexec);
>  
> -	if (O_CLOEXEC && fd < 0 && errno == EINVAL)
> +	if ((cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
>  		/* Try again w/o O_CLOEXEC: the kernel might not support it */
> -		fd = open(ce->name, O_RDONLY);
> +		cloexec &= ~O_CLOEXEC;
> +		return ce_compare_data(ce, st);
> +	}
>  

That still looks overly complicated, repeatedly ORing cloexec and
recursing without need. How about this instead?

	static int oflags = O_RDONLY | O_CLOEXEC;
	int fd = open(ce->name, oflags);

	if ((O_CLOEXEC & oflags) && fd < 0 && errno == EINVAL) {
  		/* Try again w/o O_CLOEXEC: the kernel might not support it */
		oflags &= ~O_CLOEXEC;
		fd = open(ce->name, oflags);
	}

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks
  2016-10-24 18:02 [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks larsxschneider
                   ` (2 preceding siblings ...)
  2016-10-24 18:23 ` [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks Junio C Hamano
@ 2016-10-25 11:27 ` Johannes Schindelin
  2016-10-25 18:16   ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Junio C Hamano
  3 siblings, 1 reply; 54+ messages in thread
From: Johannes Schindelin @ 2016-10-25 11:27 UTC (permalink / raw)
  To: Lars Schneider; +Cc: git, e, jnareb, gitster

Hi Lars,

On Mon, 24 Oct 2016, larsxschneider@gmail.com wrote:

> This mini patch series is necessary to make the "ls/filter-process" topic
> work properly for Windows. Right now the topic generates test failures
> on Windows as reported by Dscho:
> http://public-inbox.org/git/alpine.DEB.2.20.1610211457030.3264@virtualbox/

I acknowledge that this fixes the test failures in Git for Windows' SDK.

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC
  2016-10-25 10:27   ` Johannes Schindelin
@ 2016-10-25 16:58     ` Junio C Hamano
  0 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 16:58 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Lars Schneider, git, e, jnareb

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> The patch series may be a little bit more pleasant to read if you renamed
> git_open_noatime() to git_open() first, in a separate commit.

Possibly.

If this were "we added a new parameter at the same time and each
calling site of the renamed function with an extra parameter chooses
to pass different value to it", then keeping the rename and the
interface enhancement as two separate steps would help a lot.

But this one only renames and updates the internal implementation,
without changing what the calling sites need to do.  I am OK with
having them together in a single patch like the one posted.

>> @@ -1598,12 +1598,18 @@ int git_open_noatime(const char *name)
>>  		if (fd >= 0)
>>  			return fd;
>>  
>> -		/* Might the failure be due to O_NOATIME? */
>> -		if (errno != ENOENT && sha1_file_open_flag) {
>> -			sha1_file_open_flag = 0;
>> +		/* Try again w/o O_CLOEXEC: the kernel might not support it */
>> +		if (O_CLOEXEC && errno == EINVAL &&
>> +			(sha1_file_open_flag & O_CLOEXEC)) {
>> +			sha1_file_open_flag &= ~O_CLOEXEC;
>
> How about
>
> 		if ((O_CLOEXEC & sha1_file_open_flag) && errno == EINVAL) {
> 			sha1_file_open_flag &= ~O_CLOEXEC;
>
> instead? It is shorter and should be just as easily optimized out by a
> C compiler if O_CLOEXEC was defined as 0.

Yup, I think that makes things easier to read for humans, too.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-25 10:33       ` Johannes Schindelin
@ 2016-10-25 17:02         ` Junio C Hamano
  0 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 17:02 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Eric Wong, larsxschneider, git, jnareb

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> That still looks overly complicated, repeatedly ORing cloexec and
> recursing without need. How about this instead?
>
> 	static int oflags = O_RDONLY | O_CLOEXEC;
> 	int fd = open(ce->name, oflags);
>
> 	if ((O_CLOEXEC & oflags) && fd < 0 && errno == EINVAL) {
>   		/* Try again w/o O_CLOEXEC: the kernel might not support it */
> 		oflags &= ~O_CLOEXEC;
> 		fd = open(ce->name, oflags);
> 	}

I deliberately separated the part that can and designed to be
toggled (O_CLOEXEC) and the part that is meant to be constant
(O_RDONLY), and I do not think the first part of suggestion is
particularly a good idea.

I didn't write the same open twice, but I agree that an extra "we
fallback to opening with a different flags" inside the if () { }
block that is there exactly for implementing that fallback is an
excellent idea.  I like that part of the suggestion.

Thanks.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC
  2016-10-25 11:27 ` Johannes Schindelin
@ 2016-10-25 18:16   ` Junio C Hamano
  2016-10-25 18:16     ` [PATCH v3 1/3] sha1_file: rename git_open_noatime() to git_open() Junio C Hamano
                       ` (3 more replies)
  0 siblings, 4 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 18:16 UTC (permalink / raw)
  To: git; +Cc: Eric Wong, Johannes Schindelin, Lars Schneider

Here is to make sure everybody is on the same page regarding the
series.  The patches are adjusted for comments from Eric and Dscho.

Lars Schneider (3):
  sha1_file: rename git_open_noatime() to git_open()
  sha1_file: open window into packfiles with O_CLOEXEC
  read-cache: make sure file handles are not inherited by child
    processes

 builtin/pack-objects.c |  2 +-
 cache.h                |  2 +-
 pack-bitmap.c          |  2 +-
 read-cache.c           |  9 ++++++++-
 sha1_file.c            | 25 +++++++++++++++----------
 5 files changed, 26 insertions(+), 14 deletions(-)

-- 
2.10.1-777-gd068e6bde7


^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH v3 1/3] sha1_file: rename git_open_noatime() to git_open()
  2016-10-25 18:16   ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Junio C Hamano
@ 2016-10-25 18:16     ` Junio C Hamano
  2016-10-25 18:16     ` [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC Junio C Hamano
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 18:16 UTC (permalink / raw)
  To: git; +Cc: Lars Schneider, Eric Wong, Johannes Schindelin

From: Lars Schneider <larsxschneider@gmail.com>

This function is meant to be used when reading from files in the
object store, and the original objective was to avoid smudging atime
of loose object files too often, hence its name.  Because we'll be
extending its role in the next commit to also arrange the file
descriptors they return auto-closed in the child processes, rename
it to lose "noatime" part that is too specific.

Signed-off-by: Lars Schneider <larsxschneider@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * This is a pure-rename step suggested by Dscho.

 builtin/pack-objects.c |  2 +-
 cache.h                |  2 +-
 pack-bitmap.c          |  2 +-
 sha1_file.c            | 12 ++++++------
 4 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 1e7c2a98a5..0fd52bd6b4 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -720,7 +720,7 @@ static off_t write_reused_pack(struct sha1file *f)
 	if (!is_pack_valid(reuse_packfile))
 		die("packfile is invalid: %s", reuse_packfile->pack_name);
 
-	fd = git_open_noatime(reuse_packfile->pack_name);
+	fd = git_open(reuse_packfile->pack_name);
 	if (fd < 0)
 		die_errno("unable to open packfile for reuse: %s",
 			  reuse_packfile->pack_name);
diff --git a/cache.h b/cache.h
index 0dc39a998c..a902ca1f8e 100644
--- a/cache.h
+++ b/cache.h
@@ -1122,7 +1122,7 @@ extern int write_sha1_file(const void *buf, unsigned long len, const char *type,
 extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
-extern int git_open_noatime(const char *name);
+extern int git_open(const char *name);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index b949e51716..39bcc16846 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -266,7 +266,7 @@ static int open_pack_bitmap_1(struct packed_git *packfile)
 		return -1;
 
 	idx_name = pack_bitmap_filename(packfile);
-	fd = git_open_noatime(idx_name);
+	fd = git_open(idx_name);
 	free(idx_name);
 
 	if (fd < 0)
diff --git a/sha1_file.c b/sha1_file.c
index 266152de36..5d2bcd3ed1 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -370,7 +370,7 @@ void read_info_alternates(const char * relative_base, int depth)
 	int fd;
 
 	path = xstrfmt("%s/info/alternates", relative_base);
-	fd = git_open_noatime(path);
+	fd = git_open(path);
 	free(path);
 	if (fd < 0)
 		return;
@@ -663,7 +663,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
 	struct pack_idx_header *hdr;
 	size_t idx_size;
 	uint32_t version, nr, i, *index;
-	int fd = git_open_noatime(path);
+	int fd = git_open(path);
 	struct stat st;
 
 	if (fd < 0)
@@ -1069,7 +1069,7 @@ static int open_packed_git_1(struct packed_git *p)
 	while (pack_max_fds <= pack_open_fds && close_one_pack())
 		; /* nothing */
 
-	p->pack_fd = git_open_noatime(p->pack_name);
+	p->pack_fd = git_open(p->pack_name);
 	if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
 		return -1;
 	pack_open_fds++;
@@ -1559,7 +1559,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
 	return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
-int git_open_noatime(const char *name)
+int git_open(const char *name)
 {
 	static int sha1_file_open_flag = O_NOATIME;
 
@@ -1605,7 +1605,7 @@ static int open_sha1_file(const unsigned char *sha1)
 	struct alternate_object_database *alt;
 	int most_interesting_errno;
 
-	fd = git_open_noatime(sha1_file_name(sha1));
+	fd = git_open(sha1_file_name(sha1));
 	if (fd >= 0)
 		return fd;
 	most_interesting_errno = errno;
@@ -1613,7 +1613,7 @@ static int open_sha1_file(const unsigned char *sha1)
 	prepare_alt_odb();
 	for (alt = alt_odb_list; alt; alt = alt->next) {
 		const char *path = alt_sha1_path(alt, sha1);
-		fd = git_open_noatime(path);
+		fd = git_open(path);
 		if (fd >= 0)
 			return fd;
 		if (most_interesting_errno == ENOENT)
-- 
2.10.1-777-gd068e6bde7


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-25 18:16   ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Junio C Hamano
  2016-10-25 18:16     ` [PATCH v3 1/3] sha1_file: rename git_open_noatime() to git_open() Junio C Hamano
@ 2016-10-25 18:16     ` Junio C Hamano
  2016-10-26  4:25       ` Jeff King
  2016-10-25 18:16     ` [PATCH v3 3/3] read-cache: make sure file handles are not inherited by child processes Junio C Hamano
  2016-10-25 21:48     ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Lars Schneider
  3 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 18:16 UTC (permalink / raw)
  To: git; +Cc: Lars Schneider, Eric Wong, Johannes Schindelin

From: Lars Schneider <larsxschneider@gmail.com>

All processes that the Git main process spawns inherit the open file
descriptors of the main process. These leaked file descriptors can
cause problems.

Use the O_CLOEXEC flag similar to 05d1ed61 to fix the leaked file
descriptors.

Signed-off-by: Lars Schneider <larsxschneider@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * And the remainder of original 1/2, again taking suggestion by DScho.

 sha1_file.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/sha1_file.c b/sha1_file.c
index 5d2bcd3ed1..09045df1dc 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1561,7 +1561,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
 
 int git_open(const char *name)
 {
-	static int sha1_file_open_flag = O_NOATIME;
+	static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
 
 	for (;;) {
 		int fd;
@@ -1571,12 +1571,17 @@ int git_open(const char *name)
 		if (fd >= 0)
 			return fd;
 
-		/* Might the failure be due to O_NOATIME? */
-		if (errno != ENOENT && sha1_file_open_flag) {
-			sha1_file_open_flag = 0;
+		/* 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;
 		}
 
+		/* 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;
 	}
 }
-- 
2.10.1-777-gd068e6bde7


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH v3 3/3] read-cache: make sure file handles are not inherited by child processes
  2016-10-25 18:16   ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Junio C Hamano
  2016-10-25 18:16     ` [PATCH v3 1/3] sha1_file: rename git_open_noatime() to git_open() Junio C Hamano
  2016-10-25 18:16     ` [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC Junio C Hamano
@ 2016-10-25 18:16     ` Junio C Hamano
  2016-10-25 21:33       ` Eric Wong
  2016-10-25 21:48     ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Lars Schneider
  3 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 18:16 UTC (permalink / raw)
  To: git; +Cc: Lars Schneider, Eric Wong, Johannes Schindelin

From: Lars Schneider <larsxschneider@gmail.com>

This fixes "convert: add filter.<driver>.process option" (edcc8581) on
Windows.

Consider the case of a file that requires filtering and is present in
branch A but not in branch B. If A is the current HEAD and we checkout B
then the following happens:

1. ce_compare_data() opens the file
2.   index_fd() detects that the file requires to run a clean filter and
     calls index_stream_convert_blob()
4.     index_stream_convert_blob() calls convert_to_git_filter_fd()
5.       convert_to_git_filter_fd() calls apply_filter() which creates a
         new long running filter process (in case it is the first file
         of this kind to be filtered)
6.       The new filter process inherits all file handles. This is the
         default on Linux/OSX and is explicitly defined in the
         `CreateProcessW` call in `mingw.c` on Windows.
7. ce_compare_data() closes the file
8. Git unlinks the file as it is not present in B

The unlink operation does not work on Windows because the filter process
has still an open handle to the file. On Linux/OSX the unlink operation
succeeds but the file descriptors still leak into the child process.

Fix this problem by opening files in read-cache with the O_CLOEXEC flag
to ensure that the file descriptor does not remain open in a newly
spawned process similar to 05d1ed6148 ("mingw: ensure temporary file
handles are not inherited by child processes", 2016-08-22).

Signed-off-by: Lars Schneider <larsxschneider@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * With Eric's suggestion to avoid repeated EINVAL and fallback,
   implemented with Dscho's "write open twice explicitly, we are
   retrying after all" coding style.

 read-cache.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/read-cache.c b/read-cache.c
index 38d67faf70..db5d910642 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -156,7 +156,14 @@ 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;
-	int fd = open(ce->name, O_RDONLY);
+	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);
+	}
 
 	if (fd >= 0) {
 		unsigned char sha1[20];
-- 
2.10.1-777-gd068e6bde7


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 3/3] read-cache: make sure file handles are not inherited by child processes
  2016-10-25 18:16     ` [PATCH v3 3/3] read-cache: make sure file handles are not inherited by child processes Junio C Hamano
@ 2016-10-25 21:33       ` Eric Wong
  2016-10-25 22:54         ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Eric Wong @ 2016-10-25 21:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Lars Schneider, Johannes Schindelin

Junio C Hamano <gitster@pobox.com> wrote:
> @@ -156,7 +156,14 @@ 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;
> -	int fd = open(ce->name, O_RDONLY);
> +	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);
> +	}

Seems fine, I prefer not using recursion so it's
easier-to-review.

But I have a _slight_ preference towards Dscho's version in
<alpine.DEB.2.20.1610251230150.3264@virtualbox> in case we
decide to start using another O_* flag in here.
(but I'm not usually a C programmer)

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes
  2016-10-24 19:53     ` Lars Schneider
@ 2016-10-25 21:39       ` Johannes Sixt
  0 siblings, 0 replies; 54+ messages in thread
From: Johannes Sixt @ 2016-10-25 21:39 UTC (permalink / raw)
  To: Lars Schneider; +Cc: git, Johannes.Schindelin, e, jnareb, gitster

Am 24.10.2016 um 21:53 schrieb Lars Schneider:
>
>> On 24 Oct 2016, at 21:22, Johannes Sixt <j6t@kdbg.org> wrote:
>>
>> Am 24.10.2016 um 20:03 schrieb larsxschneider@gmail.com:
>>> From: Lars Schneider <larsxschneider@gmail.com>
>>>
>>> This fixes "convert: add filter.<driver>.process option" (edcc8581) on
>>> Windows.
>>
>> Today's next falls flat on its face on Windows in t0021.15
>> "required process filter should filter data"; might it be the
>> failure meant here?(I haven't dug deeper, yet.)
>
> Yes, this is the failure meant here :-)

I trust your word and Dscho's that it fixes a failure on Windows. In my 
case, however, it was an outdated perl (5.8.8) which left a message 
along the lines of "lookup of member flush in IO::Handle failed" in one 
of the *.log files. I came up with the following workaround.

Fix-method: shot-in-the-dark
Perl-foo: not-present
Not-signed-off-by: Me

diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl
index ae4c50f..bb88c5f 100755
--- a/t/t0021/rot13-filter.pl
+++ b/t/t0021/rot13-filter.pl
@@ -22,6 +22,7 @@

  use strict;
  use warnings;
+use FileHandle;

  my $MAX_PACKET_CONTENT_SIZE = 65516;
  my @capabilities            = @ARGV;
-- 
2.10.0.343.g37bc62b


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC
  2016-10-25 18:16   ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Junio C Hamano
                       ` (2 preceding siblings ...)
  2016-10-25 18:16     ` [PATCH v3 3/3] read-cache: make sure file handles are not inherited by child processes Junio C Hamano
@ 2016-10-25 21:48     ` Lars Schneider
  2016-10-25 22:56       ` Junio C Hamano
  3 siblings, 1 reply; 54+ messages in thread
From: Lars Schneider @ 2016-10-25 21:48 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Eric Wong, Johannes Schindelin, Johannes Sixt


> On 25 Oct 2016, at 20:16, Junio C Hamano <gitster@pobox.com> wrote:
> 
> Here is to make sure everybody is on the same page regarding the
> series.  The patches are adjusted for comments from Eric and Dscho.

Thank you, Junio! Your v3 looks good to me and I compiled and tested
it on Windows.

There is one catch though:
I ran the tests multiple times on Windows and I noticed that the
following test seems flaky:
t0021 `required process filter should be used only for "clean" operation only'

This flaky-ness was not introduced by your v3. I was able to reproduce
the same behavior for v2. I wonder why I did not noticed that before. 
The only difference to before is that my Windows machines does some 
heavy CPU/network task in the background now (I can't/don't want to
stop that ATM).

Here is the relevant part of the log:

++ rm -f rot13-filter.log
++ git checkout --quiet --no-progress .
+ test_eval_ret_=255
+ want_trace
+ test t = t
+ test t = t
+ set +x
error: last command exited with $?=255

Looks like Git exists with 255. Any idea why that might happen?

@Dscho/Johannes: Can you try this on your Windows machines?

Thanks,
Lars



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 3/3] read-cache: make sure file handles are not inherited by child processes
  2016-10-25 21:33       ` Eric Wong
@ 2016-10-25 22:54         ` Junio C Hamano
  0 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 22:54 UTC (permalink / raw)
  To: Eric Wong; +Cc: git, Lars Schneider, Johannes Schindelin

Eric Wong <e@80x24.org> writes:

> But I have a _slight_ preference towards Dscho's version in
> <alpine.DEB.2.20.1610251230150.3264@virtualbox> in case we
> decide to start using another O_* flag in here.

Interesting.  The reason why I have a slight preferene to separate
the fixed part and the toggle-able part is exactly because I want
the code to be prepared in case we decide to start using other O_*
flags in the future.

I guess different people have different tastes, as usual ;-)

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC
  2016-10-25 21:48     ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Lars Schneider
@ 2016-10-25 22:56       ` Junio C Hamano
  0 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-25 22:56 UTC (permalink / raw)
  To: Lars Schneider; +Cc: git, Eric Wong, Johannes Schindelin, Johannes Sixt

Lars Schneider <larsxschneider@gmail.com> writes:

> heavy CPU/network task in the background now (I can't/don't want to
> stop that ATM).
>
> Here is the relevant part of the log:
>
> ++ rm -f rot13-filter.log
> ++ git checkout --quiet --no-progress .
> + test_eval_ret_=255
> + want_trace
> + test t = t
> + test t = t
> + set +x
> error: last command exited with $?=255
>
> Looks like Git exists with 255. Any idea why that might happen?

Other than "the --quiet above may be hiding it?", I do not
immediately have a useful guess, sorry.

> @Dscho/Johannes: Can you try this on your Windows machines?
>
> Thanks,
> Lars

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-25 18:16     ` [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC Junio C Hamano
@ 2016-10-26  4:25       ` Jeff King
  2016-10-26 16:23         ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Jeff King @ 2016-10-26  4:25 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Lars Schneider, Eric Wong, Johannes Schindelin

On Tue, Oct 25, 2016 at 11:16:20AM -0700, Junio C Hamano wrote:

> diff --git a/sha1_file.c b/sha1_file.c
> index 5d2bcd3ed1..09045df1dc 100644
> --- a/sha1_file.c
> +++ b/sha1_file.c
> @@ -1571,12 +1571,17 @@ int git_open(const char *name)
>  		if (fd >= 0)
>  			return fd;
>  
> -		/* Might the failure be due to O_NOATIME? */
> -		if (errno != ENOENT && sha1_file_open_flag) {
> -			sha1_file_open_flag = 0;
> +		/* 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;
>  		}

So if we start with O_CLOEXEC|O_NOATIME, we drop CLOEXEC here and try
again with just O_NOATIME. And then if _that_ fails...

> +		/* Might the failure be due to O_NOATIME? */
> +		if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) {
> +			sha1_file_open_flag &= ~O_NOATIME;
> +			continue;
> +		}

We drop O_NOATIME, and end up with an empty flag field.

But we will never have tried just O_CLOEXEC, which might have worked.

Because of the order here, this would not be a regression (i.e., any
system that used to work will still eventually find a working comb), but
it does mean that systems without O_NOATIME do not get the benefit of
the new O_CLOEXEC protection.

Unfortunately, I think covering all of the cases would be 2^nr_flags.
That's only 4 right now, but it does not bode well as a pattern.

I'm not sure it's worth worrying about or not; I don't know which
systems are actually lacking either of the flags, or if they tend to
have both.

-Peff

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-26  4:25       ` Jeff King
@ 2016-10-26 16:23         ` Junio C Hamano
  2016-10-26 16:47           ` Jeff King
  0 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-26 16:23 UTC (permalink / raw)
  To: Jeff King; +Cc: git, Lars Schneider, Eric Wong, Johannes Schindelin

Jeff King <peff@peff.net> writes:

>> +		/* 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;
>>  		}
>
> So if we start with O_CLOEXEC|O_NOATIME, we drop CLOEXEC here and try
> again with just O_NOATIME. And then if _that_ fails...
>
>> +		/* Might the failure be due to O_NOATIME? */
>> +		if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) {
>> +			sha1_file_open_flag &= ~O_NOATIME;
>> +			continue;
>> +		}
>
> We drop O_NOATIME, and end up with an empty flag field.
>
> But we will never have tried just O_CLOEXEC, which might have worked.

Yes, doing so would smudge atime, so one question is which one
between noatime or cloexec is more important to be done at open(2)
time.

It may be possible to open(2) only with cloexec and then fcntl(2)
FD_SET noatime immediately after, but going that way would explode
the combination even more, as it may not be possible to set these
two flags the other way around.

> I'm not sure it's worth worrying about or not; I don't know which
> systems are actually lacking either of the flags, or if they tend to
> have both.


^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-26 16:23         ` Junio C Hamano
@ 2016-10-26 16:47           ` Jeff King
  2016-10-26 17:52             ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Jeff King @ 2016-10-26 16:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Lars Schneider, Eric Wong, Johannes Schindelin

On Wed, Oct 26, 2016 at 09:23:21AM -0700, Junio C Hamano wrote:

> >> +		/* Might the failure be due to O_NOATIME? */
> >> +		if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) {
> >> +			sha1_file_open_flag &= ~O_NOATIME;
> >> +			continue;
> >> +		}
> >
> > We drop O_NOATIME, and end up with an empty flag field.
> >
> > But we will never have tried just O_CLOEXEC, which might have worked.
> 
> Yes, doing so would smudge atime, so one question is which one
> between noatime or cloexec is more important to be done at open(2)
> time.

Yes, but the missing case is one where we know that O_NOATIME does not
work (but O_CLOEXEC does), so we know we have to smudge the atime.

Of the two flags, I would say CLOEXEC is the more important one to
respect because it may actually impact correctness (e.g., leaking
descriptors to sub-processes). Whereas O_NOATIME is purely a performance
optimization.

I actually wonder if it is worth carrying around the O_NOATIME hack at
all.  Linus added it on 2005-04-23 via 144bde78e9; the aim was to reduce
the cost of opening loose object files. Some things have changed since
then:

  1. In June 2005, git learned about packfiles, which means we would do
     a lot fewer atime updates (rather than one per object access, we'd
     generally get one per packfile).

  2. In late 2006, Linux learned about "relatime", which is generally
     the default on modern installs. So performance around atime updates
     is a non-issue there these days.

     All the world isn't Linux, of course, but I can't help that feel
     that atime performance hackery is something that belongs at the
     system level, not in individual applications.

So I don't have hard numbers, but I'd be surprised if O_NOATIME is
really buying us anything these days.

-Peff

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-26 16:47           ` Jeff King
@ 2016-10-26 17:52             ` Junio C Hamano
  2016-10-26 20:17               ` Jeff King
  0 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-26 17:52 UTC (permalink / raw)
  To: Jeff King; +Cc: git, Lars Schneider, Eric Wong, Johannes Schindelin

Jeff King <peff@peff.net> writes:

> Of the two flags, I would say CLOEXEC is the more important one to
> respect because it may actually impact correctness (e.g., leaking
> descriptors to sub-processes). Whereas O_NOATIME is purely a performance
> optimization.

I tend to agree.

> I actually wonder if it is worth carrying around the O_NOATIME hack at
> all.

Yes, I share the thought.  We no longer have too many loose objects
to matter.

I do not mind flipping the order, but I'd prefer to cook the result
even longer.  I am tempted to suggest we take two step route:

 - ship 2.11 with the "atime has been there and we won't regress it"
   shape, while cooking the "cloexec is semantically more
   important" version in 'next' during the feature freeze

 - immediately after 2.11 merge it to 'master' for 2.12 to make sure
   there is no fallout.


^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-26 17:52             ` Junio C Hamano
@ 2016-10-26 20:17               ` Jeff King
  2016-10-26 21:15                 ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Jeff King @ 2016-10-26 20:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Lars Schneider, Eric Wong, Johannes Schindelin

On Wed, Oct 26, 2016 at 10:52:41AM -0700, Junio C Hamano wrote:

> > I actually wonder if it is worth carrying around the O_NOATIME hack at
> > all.
> 
> Yes, I share the thought.  We no longer have too many loose objects
> to matter.
> 
> I do not mind flipping the order, but I'd prefer to cook the result
> even longer.  I am tempted to suggest we take two step route:
> 
>  - ship 2.11 with the "atime has been there and we won't regress it"
>    shape, while cooking the "cloexec is semantically more
>    important" version in 'next' during the feature freeze
> 
>  - immediately after 2.11 merge it to 'master' for 2.12 to make sure
>    there is no fallout.

That sounds reasonable, though I'd consider jumping straight to "NOATIME
is not worth it; drop it" as the patch for post-2.11.

-Peff

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-26 20:17               ` Jeff King
@ 2016-10-26 21:15                 ` Junio C Hamano
  2016-10-27 10:24                   ` Jeff King
  0 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-26 21:15 UTC (permalink / raw)
  To: Jeff King; +Cc: git, Lars Schneider, Eric Wong, Johannes Schindelin

Jeff King <peff@peff.net> writes:

> On Wed, Oct 26, 2016 at 10:52:41AM -0700, Junio C Hamano wrote:
>
>> > I actually wonder if it is worth carrying around the O_NOATIME hack at
>> > all.
>> 
>> Yes, I share the thought.  We no longer have too many loose objects
>> to matter.
>> 
>> I do not mind flipping the order, but I'd prefer to cook the result
>> even longer.  I am tempted to suggest we take two step route:
>> 
>>  - ship 2.11 with the "atime has been there and we won't regress it"
>>    shape, while cooking the "cloexec is semantically more
>>    important" version in 'next' during the feature freeze
>> 
>>  - immediately after 2.11 merge it to 'master' for 2.12 to make sure
>>    there is no fallout.
>
> That sounds reasonable, though I'd consider jumping straight to "NOATIME
> is not worth it; drop it" as the patch for post-2.11.

That endgame is fine by me too.  Thanks for a sanity-check.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-26 21:15                 ` Junio C Hamano
@ 2016-10-27 10:24                   ` Jeff King
  2016-10-27 21:49                     ` Junio C Hamano
  2016-10-27 22:38                     ` Linus Torvalds
  0 siblings, 2 replies; 54+ messages in thread
From: Jeff King @ 2016-10-27 10:24 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Linus Torvalds, git, Lars Schneider, Eric Wong, Johannes Schindelin

On Wed, Oct 26, 2016 at 02:15:33PM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > On Wed, Oct 26, 2016 at 10:52:41AM -0700, Junio C Hamano wrote:
> >
> >> > I actually wonder if it is worth carrying around the O_NOATIME hack at
> >> > all.
> >> 
> >> Yes, I share the thought.  We no longer have too many loose objects
> >> to matter.
> >> 
> >> I do not mind flipping the order, but I'd prefer to cook the result
> >> even longer.  I am tempted to suggest we take two step route:
> >> 
> >>  - ship 2.11 with the "atime has been there and we won't regress it"
> >>    shape, while cooking the "cloexec is semantically more
> >>    important" version in 'next' during the feature freeze
> >> 
> >>  - immediately after 2.11 merge it to 'master' for 2.12 to make sure
> >>    there is no fallout.
> >
> > That sounds reasonable, though I'd consider jumping straight to "NOATIME
> > is not worth it; drop it" as the patch for post-2.11.
> 
> That endgame is fine by me too.  Thanks for a sanity-check.

So here's that endgame patch. My main concern with it was that there
might be non-Linux systems that could be affected. But when I dug into
it, I found that this code was never activated anywhere besides Linux in
the first place. So I really doubt this will have any negative impact at
all. I certainly don't mind cooking it until post-2.11, though.

+cc Linus as the original author of 144bde78e9 in case there is
something subtle I'm missing, but this really just seems like it's
an outdated optimization.

-- >8 --
Subject: [PATCH] sha1_file: stop opening files with O_NOATIME

When we open object files, we try to do so with O_NOATIME.
This dates back to 144bde78e9 (Use O_NOATIME when opening
the sha1 files., 2005-04-23), which is an optimization to
avoid creating a bunch of dirty inodes when we're accessing
many objects.  But a few things have changed since then:

  1. In June 2005, git learned about packfiles, which means
     we would do a lot fewer atime updates (rather than one
     per object access, we'd generally get one per packfile).

  2. In late 2006, Linux learned about "relatime", which is
     generally the default on modern installs. So
     performance around atimes updates is a non-issue there
     these days.

     All the world isn't Linux, but as it turns out, Linux
     is the only platform to implement O_NOATIME in the
     first place.

So it's very unlikely that this code is helping anybody
these days.

It's not a particularly large amount of code, but the
fallback-retry creates complexity. E.g., we do a similar
fallback for CLOEXEC; which one should take precedence, or
should we try all possible combinations? Dropping O_NOATIME
makes those questions go away.

Signed-off-by: Jeff King <peff@peff.net>
---
 sha1_file.c | 15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/sha1_file.c b/sha1_file.c
index 09045df1dc..6f02a57d8b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -27,14 +27,6 @@
 #include "list.h"
 #include "mergesort.h"
 
-#ifndef O_NOATIME
-#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
-#define O_NOATIME 01000000
-#else
-#define O_NOATIME 0
-#endif
-#endif
-
 #define SZ_FMT PRIuMAX
 static inline uintmax_t sz_fmt(size_t s) { return s; }
 
@@ -1561,7 +1553,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
 
 int git_open(const char *name)
 {
-	static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
+	static int sha1_file_open_flag = O_CLOEXEC;
 
 	for (;;) {
 		int fd;
@@ -1577,11 +1569,6 @@ int git_open(const char *name)
 			continue;
 		}
 
-		/* 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;
 	}
 }
-- 
2.10.1.916.g0d2035c


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 10:24                   ` Jeff King
@ 2016-10-27 21:49                     ` Junio C Hamano
  2016-10-27 22:38                     ` Linus Torvalds
  1 sibling, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-27 21:49 UTC (permalink / raw)
  To: Jeff King
  Cc: Linus Torvalds, git, Lars Schneider, Eric Wong, Johannes Schindelin

Jeff King <peff@peff.net> writes:

> +cc Linus as the original author of 144bde78e9 in case there is
> something subtle I'm missing, but this really just seems like it's
> an outdated optimization.
>
> -- >8 --
> Subject: [PATCH] sha1_file: stop opening files with O_NOATIME
>
> When we open object files, we try to do so with O_NOATIME.
> This dates back to 144bde78e9 (Use O_NOATIME when opening
> the sha1 files., 2005-04-23), which is an optimization to
> avoid creating a bunch of dirty inodes when we're accessing
> many objects.  But a few things have changed since then:
>
>   1. In June 2005, git learned about packfiles, which means
>      we would do a lot fewer atime updates (rather than one
>      per object access, we'd generally get one per packfile).
>
>   2. In late 2006, Linux learned about "relatime", which is
>      generally the default on modern installs. So
>      performance around atimes updates is a non-issue there
>      these days.
>
>      All the world isn't Linux, but as it turns out, Linux
>      is the only platform to implement O_NOATIME in the
>      first place.
>
> So it's very unlikely that this code is helping anybody
> these days.
>
> It's not a particularly large amount of code, but the
> fallback-retry creates complexity. E.g., we do a similar
> fallback for CLOEXEC; which one should take precedence, or
> should we try all possible combinations? Dropping O_NOATIME
> makes those questions go away.
>
> Signed-off-by: Jeff King <peff@peff.net>
> ---

We may want to lose the surrounding for (;;) loop as there is only
one flag to retry without, which was the original code structure
back when 144bde78e9 ("Use O_NOATIME when opening the sha1 files.",
2005-04-23) was written and refactored by 44d1c19ee8 ("Make loose
object file reading more careful", 2008-06-14).

IOW, this on top.  The update to ce_compare_data() Lars has in
a0a6cb9662 ("read-cache: make sure file handles are not inherited by
child processes", 2016-10-24) could then made into a call to
git_open().

 sha1_file.c | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/sha1_file.c b/sha1_file.c
index 6f02a57d8b..e18ea053e6 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1553,24 +1553,17 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
 
 int git_open(const char *name)
 {
-	static int sha1_file_open_flag = O_CLOEXEC;
-
-	for (;;) {
-		int fd;
-
-		errno = 0;
-		fd = open(name, O_RDONLY | sha1_file_open_flag);
-		if (fd >= 0)
-			return fd;
+	static int cloexec = O_CLOEXEC;
+	int fd;
 
+	errno = 0;
+	fd = open(name, O_RDONLY | cloexec);
+	if ((cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
 		/* 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;
-		}
-
-		return -1;
+		cloexec &= ~O_CLOEXEC;
+		fd = open(name, O_RDONLY | cloexec);
 	}
+	return fd;
 }
 
 static int stat_sha1_file(const unsigned char *sha1, struct stat *st)

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 10:24                   ` Jeff King
  2016-10-27 21:49                     ` Junio C Hamano
@ 2016-10-27 22:38                     ` Linus Torvalds
  2016-10-27 22:56                       ` Junio C Hamano
  2016-10-28  7:51                       ` Jeff King
  1 sibling, 2 replies; 54+ messages in thread
From: Linus Torvalds @ 2016-10-27 22:38 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

On Thu, Oct 27, 2016 at 3:24 AM, Jeff King <peff@peff.net> wrote:
>
> +cc Linus as the original author of 144bde78e9 in case there is
> something subtle I'm missing, but this really just seems like it's
> an outdated optimization.

I'd *really* like to keep O_NOATIME if at all possible. It made a huge
difference on older kernels, and I'm not convinced that relatime
really fixes it as well as O_NOATIME.

There are people who don't like relatime. And even if you do have
relatime enabled, it will update atime once every day, so then this
makes your filesystem have a storm of nasty inode writebacks if you
haven't touched that git repo in a while.

If this is purely about mixing things up with O_CLOEXEC, then that is
*trivially* fixable by just using

     fcntl(fd, F_SETFD, FD_CLOEXEC);

after the open.

Which is what you have to do anyway if you want to be portable, so
its' not like you could avoid that complexity in the first place.

Note that you can *not* do the same thing with O_NOATIME, since the
whole point of O_NOATIME is that it changes the behavior of the open
itself (unlike O_CLOEXEC which changes _later_ behavior, and can
always be replaced by FD_CLOEXEC fnclt modulo races that are
immaterial for git)

             Linus

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 22:38                     ` Linus Torvalds
@ 2016-10-27 22:56                       ` Junio C Hamano
  2016-10-27 23:09                         ` Linus Torvalds
  2016-10-28  7:51                       ` Jeff King
  1 sibling, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-27 22:56 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

Linus Torvalds <torvalds@linux-foundation.org> writes:

> Note that you can *not* do the same thing with O_NOATIME, since the
> whole point of O_NOATIME is that it changes the behavior of the open
> itself (unlike O_CLOEXEC which changes _later_ behavior, and can
> always be replaced by FD_CLOEXEC fnclt modulo races that are
> immaterial for git)

A tangent.  

I also thought O_NOATIME shouldn't be an effective fcntl(2) thing,
for the reasons you stated above, when I was studying the area
because of the discussion on these patches.  I was surprised to see
that http://man7.org/linux/man-pages/man2/fcntl.2.html contradicts
by saying F_SETFL can take O_NOATIME.  

Perhaps it deserves a bugreport?



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 22:56                       ` Junio C Hamano
@ 2016-10-27 23:09                         ` Linus Torvalds
  2016-10-27 23:19                           ` Linus Torvalds
  0 siblings, 1 reply; 54+ messages in thread
From: Linus Torvalds @ 2016-10-27 23:09 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

On Thu, Oct 27, 2016 at 3:56 PM, Junio C Hamano <gitster@pobox.com> wrote:
>
> I also thought O_NOATIME shouldn't be an effective fcntl(2) thing,
> for the reasons you stated above, when I was studying the area
> because of the discussion on these patches.  I was surprised to see
> that http://man7.org/linux/man-pages/man2/fcntl.2.html contradicts
> by saying F_SETFL can take O_NOATIME.
>
> Perhaps it deserves a bugreport?

It's not a bug per se.

You can indeed change O_NOATIME with F_SETFL. And it actually does
matter, since atime is normally changed by mmap(), read() and write()
calls.

So F_SETFL O_NOATIME actually does make sense, but it doesn't cover
the atime update done by open() itself.

That said, now that I think about it, I should double-check: maybe
open() doesn't actually set atime at all, and we *could* do NOATIME
with SETFL after all.

The exact [acm]time semantics are damn subtle.

             Linus

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 23:09                         ` Linus Torvalds
@ 2016-10-27 23:19                           ` Linus Torvalds
  2016-10-27 23:36                             ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Linus Torvalds @ 2016-10-27 23:19 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

On Thu, Oct 27, 2016 at 4:09 PM, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> That said, now that I think about it, I should double-check: maybe
> open() doesn't actually set atime at all, and we *could* do NOATIME
> with SETFL after all.

Checked. Yup. O_NOATIME could easily be done with SETFL:, because as
with O_CLOEXEC, it only affects operations _after_ the open. The open
itself doesn't set the access time.

So I was full of it.

But the basic issue still remains - I'd really prefer to have NOATIME
stay around for all those poor misguided souls that for some reason
don't like "relatime" or run old kernels. But whether it is with
O_NOATIME at open time or with F_SETFL, I don't care.

            Linus

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 23:19                           ` Linus Torvalds
@ 2016-10-27 23:36                             ` Junio C Hamano
  2016-10-27 23:44                               ` Linus Torvalds
  0 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-27 23:36 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

Linus Torvalds <torvalds@linux-foundation.org> writes:

> But the basic issue still remains - I'd really prefer to have NOATIME
> stay around for all those poor misguided souls that for some reason
> don't like "relatime" or run old kernels. But whether it is with
> O_NOATIME at open time or with F_SETFL, I don't care.

Understood.  

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.




^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 23:36                             ` Junio C Hamano
@ 2016-10-27 23:44                               ` Linus Torvalds
  2016-10-28  1:08                                 ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Linus Torvalds @ 2016-10-27 23:44 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

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.

Note that the O_NOATIME code is very much designed to try O_NOATIME
only _once_. Because even when the kernel supports O_NOATIME, if you
have a shared object tree where you may not be the owner of all the
files, a O_NOATIME open can fail with NOPERM ("You are not allowed to
hide your accesses to this file").

This is why it uses that

    static unsigned int sha1_file_open_flag = O_NOATIME;

and if the O_NOATIME open ever fails (and the no-O_NOATIME open
succeeds), it clears that flag. Exactly so that it will *not* end up
in some kind of "let's open and fail and re-open" loop. It's designed
to fail once.

Or at least that's how it used to be originally. This code has
obviously changed since that early design. Now it seems to clear it
for any non-ENOENT error. Which looks fine too.

          Linus

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 23:44                               ` Linus Torvalds
@ 2016-10-28  1:08                                 ` Junio C Hamano
  2016-10-28  2:37                                   ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-28  1:08 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

Linus Torvalds <torvalds@linux-foundation.org> writes:

> 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)

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28  1:08                                 ` Junio C Hamano
@ 2016-10-28  2:37                                   ` Junio C Hamano
  2016-10-28  5:51                                     ` Eric Wong
                                                       ` (2 more replies)
  0 siblings, 3 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-28  2:37 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

Junio C Hamano <gitster@pobox.com> writes:

> Linus Torvalds <torvalds@linux-foundation.org> writes:
>
>> 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.

Hmph.  This may not fly well in practice, though.  

To Unix folks, CLOEXEC is not a huge correctness issue.  A child
process may hold onto an open file descriptor a bit longer than the
lifetime of the parent but as long as the child eventually exits,
nothing is affected.  Over there, things are different.  The parent
cannot even rename(2) or unlink(2) a file it created and closed
while the child is still holding the file descriptor open and the
lack of CLOEXEC will make the parent fail.  I do not know how well
fcntl(2) emulation works on Windows, but I would not be surprised
if J6t or Dscho comes back and says that FD_CLOEXEC given to F_SETFD
would not work while O_CLOEXEC given to open(2) does.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28  2:37                                   ` Junio C Hamano
@ 2016-10-28  5:51                                     ` Eric Wong
  2016-10-28 11:11                                     ` Johannes Schindelin
  2016-10-28 13:32                                     ` Junio C Hamano
  2 siblings, 0 replies; 54+ messages in thread
From: Eric Wong @ 2016-10-28  5:51 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Linus Torvalds, Jeff King, Git Mailing List, Lars Schneider,
	Johannes Schindelin

Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
> > Linus Torvalds <torvalds@linux-foundation.org> writes:
> >
> >> 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.

Actually, I would flip the order of flags.  O_CLOEXEC is more
important from a correctness standpoint.

> > OK, so perhaps like this.
> 
> Hmph.  This may not fly well in practice, though.  
> 
> To Unix folks, CLOEXEC is not a huge correctness issue.  A child
> process may hold onto an open file descriptor a bit longer than the
> lifetime of the parent but as long as the child eventually exits,

I'm not too familiar with C internals of git; but I know we use
threads in some places, and fork+execve in others.

If our usage of threads and execve intersects, and we run
untrusted code in an execve-ed child, then only having cloexec
on open() will save us time when auditing for leaking FDs.

fcntl(fd, F_SETFD, O_CLOEXEC) is racy in if there are other
threads doing execve; so I wouldn't rely on it as a first
choice.

So I suppose something like this:

	static int noatime = 1;
	int fd = open(... | O_CLOEXEC);
	...error checking and retrying...

	if (fd >= 0 && noatime && fcntl(fd, F_SETFL, O_NOATIME) != 0)
		noatime = 0;

	return fd;

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-27 22:38                     ` Linus Torvalds
  2016-10-27 22:56                       ` Junio C Hamano
@ 2016-10-28  7:51                       ` Jeff King
  1 sibling, 0 replies; 54+ messages in thread
From: Jeff King @ 2016-10-28  7:51 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Junio C Hamano, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

On Thu, Oct 27, 2016 at 03:38:59PM -0700, Linus Torvalds wrote:

> On Thu, Oct 27, 2016 at 3:24 AM, Jeff King <peff@peff.net> wrote:
> >
> > +cc Linus as the original author of 144bde78e9 in case there is
> > something subtle I'm missing, but this really just seems like it's
> > an outdated optimization.
> 
> I'd *really* like to keep O_NOATIME if at all possible. It made a huge
> difference on older kernels, and I'm not convinced that relatime
> really fixes it as well as O_NOATIME.
> 
> There are people who don't like relatime. And even if you do have
> relatime enabled, it will update atime once every day, so then this
> makes your filesystem have a storm of nasty inode writebacks if you
> haven't touched that git repo in a while.

The existence of "relatime" is only half the story of its outdatedness.

The other half is packfiles, so that we are paying atime only once per
packfile, not once per object (technically once per mmap(), so on a
32-bit system with large packfiles, it would be multiple, depending on
your window size).

So I'm not convinced that "storm" is really the right word in a modern
context. The atime updates due to object accesses are probably smaller
than those from all the other read() calls being done on non-object
files (like config, refs, etc).

That being said, if you really care, it's not that much code to keep.

-Peff

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28  2:37                                   ` Junio C Hamano
  2016-10-28  5:51                                     ` Eric Wong
@ 2016-10-28 11:11                                     ` Johannes Schindelin
  2016-10-28 16:13                                       ` Linus Torvalds
  2016-10-28 13:32                                     ` Junio C Hamano
  2 siblings, 1 reply; 54+ messages in thread
From: Johannes Schindelin @ 2016-10-28 11:11 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Linus Torvalds, Jeff King, Git Mailing List, Lars Schneider, Eric Wong

Hi,

On Thu, 27 Oct 2016, Junio C Hamano wrote:

> Junio C Hamano <gitster@pobox.com> writes:
> 
> > Linus Torvalds <torvalds@linux-foundation.org> writes:
> >
> >> 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.
> 
> Hmph.  This may not fly well in practice, though.  
> 
> To Unix folks, CLOEXEC is not a huge correctness issue.  A child
> process may hold onto an open file descriptor a bit longer than the
> lifetime of the parent but as long as the child eventually exits,
> nothing is affected.  Over there, things are different.  The parent
> cannot even rename(2) or unlink(2) a file it created and closed
> while the child is still holding the file descriptor open and the
> lack of CLOEXEC will make the parent fail.  I do not know how well
> fcntl(2) emulation works on Windows, but I would not be surprised
> if J6t or Dscho comes back and says that FD_CLOEXEC given to F_SETFD
> would not work while O_CLOEXEC given to open(2) does.

You guys. I mean: You guys! You sure make my life hard. A brief look at
mingw.h could have answered your implicit question:

	static inline int fcntl(int fd, int cmd, ...)
	{
		if (cmd == F_GETFD || cmd == F_SETFD)
			return 0;
		errno = EINVAL;
		return -1;
	}

So while you discuss in your Linux Ivory Tower how to optimize Git for
Linux, and Linux only, I'll have to drop everything else and spend the
rest of my Friday trying to find a way to adjust a file handle
*immediately after opening it with undesired flags* (when it could have
been opened with the desired flags, as suggested, to begin with).

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28  2:37                                   ` Junio C Hamano
  2016-10-28  5:51                                     ` Eric Wong
  2016-10-28 11:11                                     ` Johannes Schindelin
@ 2016-10-28 13:32                                     ` Junio C Hamano
  2016-10-28 13:33                                       ` Junio C Hamano
  2 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-28 13:32 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

Junio C Hamano <gitster@pobox.com> writes:

> Hmph.  This may not fly well in practice, though.  

We can flip the order around, and then it becomes simpler to later
drop atime support.  The first step goes like this.

-- >8 --
From: Junio C Hamano <gitster@pobox.com>
Date: Fri, 28 Oct 2016 06:23:07 -0700
Subject: [PATCH] git_open(): untangle possible NOATIME and CLOEXEC interactions

The way we structured the fallback/retry mechanism 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.

Instead use O_CLOEXEC to open the file, and then use fcntl(2) to set
O_NOATIME on the resulting file descriptor.  open(2) itself does not
cause atime to be updated according to Linus [*1*].

The helper to do the former can be usable in the codepath in
ce_compare_data() that was recently added to open a file descriptor
with O_CLOEXEC; use it while we are at it.

*1* <CA+55aFw83E+zOd+z5h-CA-3NhrLjVr-anL6pubrSWttYx3zu8g@mail.gmail.com>

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 cache.h      |  1 +
 read-cache.c |  9 +--------
 sha1_file.c  | 39 +++++++++++++++++++--------------------
 3 files changed, 21 insertions(+), 28 deletions(-)

diff --git a/cache.h b/cache.h
index a902ca1f8e..6f1c21a352 100644
--- a/cache.h
+++ b/cache.h
@@ -1122,6 +1122,7 @@ extern int write_sha1_file(const void *buf, unsigned long len, const char *type,
 extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
+extern int git_open_cloexec(const char *name, int flags);
 extern int git_open(const char *name);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
diff --git a/read-cache.c b/read-cache.c
index db5d910642..c27d3e240b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -156,14 +156,7 @@ 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 = git_open_cloexec(ce->name, O_RDONLY);
 
 	if (fd >= 0) {
 		unsigned char sha1[20];
diff --git a/sha1_file.c b/sha1_file.c
index 09045df1dc..dfbf398183 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1559,31 +1559,30 @@ 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_open_cloexec(const char *name, int flags)
 {
-	static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
-
-	for (;;) {
-		int fd;
-
-		errno = 0;
-		fd = open(name, O_RDONLY | sha1_file_open_flag);
-		if (fd >= 0)
-			return fd;
+	static int cloexec = O_CLOEXEC;
+	int fd = open(name, flags | cloexec);
 
+	if ((cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
 		/* 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;
-		}
+		cloexec &= ~O_CLOEXEC;
+		fd = open(name, flags | cloexec);
+	}
+	return fd;
+}
 
-		/* 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;
+int git_open(const char *name)
+{
+	static int noatime = O_NOATIME;
+	int fd = git_open_cloexec(name, O_RDONLY);
+
+	if (0 <= fd && (noatime & O_NOATIME)) {
+		int flags = fcntl(fd, F_GETFL);
+		if (fcntl(fd, F_SETFL, flags | noatime))
+			noatime = 0;
 	}
+	return fd;
 }
 
 static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
-- 
2.10.1-791-g404733b9cf


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28 13:32                                     ` Junio C Hamano
@ 2016-10-28 13:33                                       ` Junio C Hamano
  0 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-28 13:33 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Jeff King, Git Mailing List, Lars Schneider, Eric Wong,
	Johannes Schindelin

Junio C Hamano <gitster@pobox.com> writes:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> Hmph.  This may not fly well in practice, though.  
>
> We can flip the order around, and then it becomes simpler to later
> drop atime support.  The first step goes like this.

And the second step would be like this.

-- >8 --
Subject: [PATCH] sha1_file: stop opening files with O_NOATIME

When we open object files, we try to do so with O_NOATIME.
This dates back to 144bde78e9 (Use O_NOATIME when opening
the sha1 files., 2005-04-23), which is an optimization to
avoid creating a bunch of dirty inodes when we're accessing
many objects.  But a few things have changed since then:

  1. In June 2005, git learned about packfiles, which means
     we would do a lot fewer atime updates (rather than one
     per object access, we'd generally get one per packfile).

  2. In late 2006, Linux learned about "relatime", which is
     generally the default on modern installs. So
     performance around atimes updates is a non-issue there
     these days.

     All the world isn't Linux, but as it turns out, Linux
     is the only platform to implement O_NOATIME in the
     first place.

So it's very unlikely that this code is helping anybody
these days.

Helped-by: Jeff King <peff@peff.net>
[jc: took idea and log message from peff]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 cache.h     |  2 +-
 sha1_file.c | 21 ---------------------
 2 files changed, 1 insertion(+), 22 deletions(-)

diff --git a/cache.h b/cache.h
index 6f1c21a352..f440d3fd1e 100644
--- a/cache.h
+++ b/cache.h
@@ -1123,7 +1123,7 @@ extern int hash_sha1_file_literally(const void *buf, unsigned long len, const ch
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 extern int git_open_cloexec(const char *name, int flags);
-extern int git_open(const char *name);
+#define git_open(name) git_open_cloexec(name, O_RDONLY)
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
diff --git a/sha1_file.c b/sha1_file.c
index dfbf398183..25d9359402 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -27,14 +27,6 @@
 #include "list.h"
 #include "mergesort.h"
 
-#ifndef O_NOATIME
-#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
-#define O_NOATIME 01000000
-#else
-#define O_NOATIME 0
-#endif
-#endif
-
 #define SZ_FMT PRIuMAX
 static inline uintmax_t sz_fmt(size_t s) { return s; }
 
@@ -1572,19 +1564,6 @@ int git_open_cloexec(const char *name, int flags)
 	return fd;
 }
 
-int git_open(const char *name)
-{
-	static int noatime = O_NOATIME;
-	int fd = git_open_cloexec(name, O_RDONLY);
-
-	if (0 <= fd && (noatime & O_NOATIME)) {
-		int flags = fcntl(fd, F_GETFL);
-		if (fcntl(fd, F_SETFL, flags | noatime))
-			noatime = 0;
-	}
-	return fd;
-}
-
 static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
 {
 	struct alternate_object_database *alt;
-- 
2.10.1-791-g404733b9cf


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28 11:11                                     ` Johannes Schindelin
@ 2016-10-28 16:13                                       ` Linus Torvalds
  2016-10-28 16:48                                         ` Junio C Hamano
  2016-10-31 13:56                                         ` Jeff King
  0 siblings, 2 replies; 54+ messages in thread
From: Linus Torvalds @ 2016-10-28 16:13 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, Jeff King, Git Mailing List, Lars Schneider, Eric Wong

On Fri, Oct 28, 2016 at 4:11 AM, Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
>
> You guys. I mean: You guys! You sure make my life hard. A brief look at
> mingw.h could have answered your implicit question:

So here's what you guys should do:

 - leave O_NOATIME damn well alone. It works. It has worked for 10+
years. Stop arguing against it, people who do.

 - get rid of all O_CLOEXEC games. They don't work. If you want to
close file descriptors at execve(), you - gasp - close the file
descriptor before doing an execve.

So O_CLOEXEC or FD_CLOEXEC is broken.

DO NOT BREAK O_NOATIME JUST TO ADD COMPLETELY NEW BREAKAGE.

                 Linus

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28 16:13                                       ` Linus Torvalds
@ 2016-10-28 16:48                                         ` Junio C Hamano
  2016-10-28 17:38                                           ` Linus Torvalds
  2016-10-31 13:56                                         ` Jeff King
  1 sibling, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-28 16:48 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Johannes Schindelin, Jeff King, Git Mailing List, Lars Schneider,
	Eric Wong

Linus Torvalds <torvalds@linux-foundation.org> writes:

> On Fri, Oct 28, 2016 at 4:11 AM, Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
>>
>> You guys. I mean: You guys! You sure make my life hard. A brief look at
>> mingw.h could have answered your implicit question:
>
> So here's what you guys should do:
>
>  - leave O_NOATIME damn well alone. It works. It has worked for 10+
> years. Stop arguing against it, people who do.
>
>  - get rid of all O_CLOEXEC games. They don't work. If you want to
> close file descriptors at execve(), you - gasp - close the file
> descriptor before doing an execve.
>
> So O_CLOEXEC or FD_CLOEXEC is broken.
>
> DO NOT BREAK O_NOATIME JUST TO ADD COMPLETELY NEW BREAKAGE.

Excuse me, but care to elaborate a bit more?

Did I botch the way I ask fcntl(2) to set O_NOATIME in *1*?  Its
follow-up in *2* is optional and as peff said in *3*, I do not think
we mind dropping that step and keeping O_NOATIME.  It is not that
much code to keep.

Setting FD_CLOEXEC with fcntl(2) may be racy, but in what way
it is broken to open(2) with O_CLOEXEC?

If we want to close file descriptors we opened at execve() time
ourselves, we'd need to somehow keep track of which file descriptors
are meant to be closed upon execve() time, and one way to mark that
may be to use O_CLOEXEC, but once we mark a file descriptor with the
bit, execve() would take care of "closing" it for us---wouldn't that
be the whole point of that bit?


*1* http://public-inbox.org/git/xmqqh97w38gj.fsf@gitster.mtv.corp.google.com
*2* http://public-inbox.org/git/xmqqd1ik38f4.fsf@gitster.mtv.corp.google.com
*3* http://public-inbox.org/git/20161028075104.la24zydnr3ogb6qv@sigill.intra.peff.net

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28 16:48                                         ` Junio C Hamano
@ 2016-10-28 17:38                                           ` Linus Torvalds
  2016-10-28 17:47                                             ` Junio C Hamano
  2016-10-29  1:26                                             ` Junio C Hamano
  0 siblings, 2 replies; 54+ messages in thread
From: Linus Torvalds @ 2016-10-28 17:38 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, Jeff King, Git Mailing List, Lars Schneider,
	Eric Wong

On Fri, Oct 28, 2016 at 9:48 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Excuse me, but care to elaborate a bit more?

It just seems entirely pointless to change the games with O_xyz.

> Setting FD_CLOEXEC with fcntl(2) may be racy, but in what way
> it is broken to open(2) with O_CLOEXEC?

Apparently windows doesn't even support it, at least not mingw. So
you'll have to have some supprt for just explicitly closing things at
execve anyway. And if you do that for windows, then what's the point
of using O_CLOEXEC on Linux, and having two totally different paths
that will just mean maintenance headache.

In contrast, O_NOATIME isn't a maintenance problem, since it's purely
an optimization and has no semantic difference, so it not existing on
some platform is immaterial.

End result: leave O_NOATIME as it is (working), and just forget about O_CLOEXEC.

I have no actual idea about mingw support for this, but Johannes'
email certainly implies it's painful on Windows. Some googling also
shows people doing things like

  #ifdef O_CLOEXEC
          flags |= O_CLOEXEC;
  #endif

or

  #ifndef O_CLOEXEC
  #define O_CLOEXEC 0
  #endif

so O_CLOEXEC definitely isn't considered portable. Do you really want
to rely on it?

              Linus

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28 17:38                                           ` Linus Torvalds
@ 2016-10-28 17:47                                             ` Junio C Hamano
  2016-10-29  1:26                                             ` Junio C Hamano
  1 sibling, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-28 17:47 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Johannes Schindelin, Jeff King, Git Mailing List, Lars Schneider,
	Eric Wong

Linus Torvalds <torvalds@linux-foundation.org> writes:

> On Fri, Oct 28, 2016 at 9:48 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Setting FD_CLOEXEC with fcntl(2) may be racy, but in what way
>> it is broken to open(2) with O_CLOEXEC?
>
> Apparently windows doesn't even support it, at least not mingw. So
> you'll have to have some supprt for just explicitly closing things at
> execve anyway. And if you do that for windows, then what's the point
> of using O_CLOEXEC on Linux, and having two totally different paths
> that will just mean maintenance headache.

Ah, that's where your reaction comes from.  If I understood Dscho
correctly, what he said was that I cannot set FD_CLOEXEC via fcntl()
emulation they have.  It wasn't an objection to O_CLOEXEC.  In fact,
since 05d1ed6148 ("mingw: ensure temporary file handles are not
inherited by child processes", 2016-08-22), we have been relying on
open(2) with O_CLOEXEC even on Windows (by emulating it with
O_NOINHERIT, which Windows has) on some codepaths.



^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28 17:38                                           ` Linus Torvalds
  2016-10-28 17:47                                             ` Junio C Hamano
@ 2016-10-29  1:26                                             ` Junio C Hamano
  2016-10-29  8:25                                               ` Johannes Schindelin
  1 sibling, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-29  1:26 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Johannes Schindelin, Jeff King, Git Mailing List, Lars Schneider,
	Eric Wong

Linus Torvalds <torvalds@linux-foundation.org> writes:

> Apparently windows doesn't even support it, at least not mingw....

Assuming that the above was a misunderstanding, assuming that we can
do O_CLOEXEC (but not FD_CLOEXEC) on Windows just fine, where stray
file descriptors held open in the children matter more, and ...

> In contrast, O_NOATIME isn't a maintenance problem, since it's purely
> an optimization and has no semantic difference, so it not existing on
> some platform is immaterial.

... assuming that I didn't screw up my use of fcntl() to set
O_NOATIME on a fd opened with O_CLOEXEC, are you OK with the
approach in patch *1*?  We can drop *2* to keep O_NOATIME, which has
been working for those with old kernels with many loose objects, and
it is not too much code to keep.

*1* http://public-inbox.org/git/xmqqh97w38gj.fsf@gitster.mtv.corp.google.com
*2* http://public-inbox.org/git/xmqqd1ik38f4.fsf@gitster.mtv.corp.google.com

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-29  1:26                                             ` Junio C Hamano
@ 2016-10-29  8:25                                               ` Johannes Schindelin
  2016-10-29 17:06                                                 ` Linus Torvalds
  0 siblings, 1 reply; 54+ messages in thread
From: Johannes Schindelin @ 2016-10-29  8:25 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Linus Torvalds, Jeff King, Git Mailing List, Lars Schneider, Eric Wong

Hi,

On Fri, 28 Oct 2016, Junio C Hamano wrote:

> Linus Torvalds <torvalds@linux-foundation.org> writes:
> 
> > Apparently windows doesn't even support it, at least not mingw....
> 
> Assuming that the above was a misunderstanding, assuming that we can
> do O_CLOEXEC (but not FD_CLOEXEC) on Windows just fine, where stray
> file descriptors held open in the children matter more, and ...

Correct. We cannot change an open file handle's state ("FD_CLOEXEC") on
Windows, but we can ask open() to open said file handle with the correct
flag ("O_CLOEXEC", which is mapped to O_NOINHERIT on Windows.

And for the record: I agree with Junio that we cannot simply drop this
O_CLOEXEC business.

Because it was introduced to fix bugs.

Thank you for your time,
Johannes

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-29  8:25                                               ` Johannes Schindelin
@ 2016-10-29 17:06                                                 ` Linus Torvalds
  2016-10-31 17:37                                                   ` Junio C Hamano
  0 siblings, 1 reply; 54+ messages in thread
From: Linus Torvalds @ 2016-10-29 17:06 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Junio C Hamano, Jeff King, Git Mailing List, Lars Schneider, Eric Wong

On Sat, Oct 29, 2016 at 1:25 AM, Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
>
> Correct. We cannot change an open file handle's state ("FD_CLOEXEC") on
> Windows, but we can ask open() to open said file handle with the correct
> flag ("O_CLOEXEC", which is mapped to O_NOINHERIT on Windows.

Ok. So then I have no issues with it, and let's use O_CLOEXEC if it
exists and fcntl(FD_CLOEXEC) if O_CLOEXEC doesn't exist.

              Linus

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-28 16:13                                       ` Linus Torvalds
  2016-10-28 16:48                                         ` Junio C Hamano
@ 2016-10-31 13:56                                         ` Jeff King
  2016-10-31 17:55                                           ` Junio C Hamano
  1 sibling, 1 reply; 54+ messages in thread
From: Jeff King @ 2016-10-31 13:56 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Johannes Schindelin, Junio C Hamano, Git Mailing List,
	Lars Schneider, Eric Wong

On Fri, Oct 28, 2016 at 09:13:41AM -0700, Linus Torvalds wrote:

> On Fri, Oct 28, 2016 at 4:11 AM, Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
> >
> > You guys. I mean: You guys! You sure make my life hard. A brief look at
> > mingw.h could have answered your implicit question:
> 
> So here's what you guys should do:
> 
>  - leave O_NOATIME damn well alone. It works. It has worked for 10+
> years. Stop arguing against it, people who do.

For some definition of worked, perhaps.

If you set a probe on touch_atime() in the kernel (which is called for
every attempt to smudge the atime, regardless of mount options, but is
skipped when the descriptor was opened with O_NOATIME), you can see the
impact. Here's a command I picked because it reads a lot of objects (run
on my git.git clone):

  $ perf stat -e probe:touch-atime git log -Sfoo >/dev/null

And the probe:touch_atime counts before (stock git) and after (a patch
to drop O_NOATIME):

  before: 22,235
   after: 22,362

So that's only half a percent difference. And it's on a reasonably messy
clone that is partway to triggering an auto-repack:

  $ git count-objects -v
  count: 6167
  size: 61128
  in-pack: 275773
  packs: 18
  size-pack: 86857
  prune-packable: 25
  garbage: 0
  size-garbage: 0

Running "git gc" drops the probe count to 21,733.

It makes a bigger difference for some commands (it's more like 10% for
git-status). And smaller for others ("git log -p" triggers it over
100,000 times).

One thing missing in that count is how many of those calls would have
resulted in an actual disk write. Looking at strace, most of the
filesystem activity is opening .gitattributes files, and we end up
opening the same ones repeatedly (e.g., t/.gitattributes in git.git).
Multiple hits for a given inode in the same second get coalesced into at
most a single disk write.

So I guess it's possible that it produces a noticeable effect in some
cases, but I'm still somewhat doubtful. And actually repacking your
repository had a greater effect in every case I measured (in addition to
providing other speedups).

Like I said, I'm OK keeping O_NOATIME. It's just not that much code. But
if you really care about the issue of dirtying inodes via atime, you
should look into vastly increasing our use of O_NOATIME. Or possibly
looking at caching more in the attribute code.

-Peff

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-29 17:06                                                 ` Linus Torvalds
@ 2016-10-31 17:37                                                   ` Junio C Hamano
  0 siblings, 0 replies; 54+ messages in thread
From: Junio C Hamano @ 2016-10-31 17:37 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Johannes Schindelin, Jeff King, Git Mailing List, Lars Schneider,
	Eric Wong

Linus Torvalds <torvalds@linux-foundation.org> writes:

> On Sat, Oct 29, 2016 at 1:25 AM, Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
>>
>> Correct. We cannot change an open file handle's state ("FD_CLOEXEC") on
>> Windows, but we can ask open() to open said file handle with the correct
>> flag ("O_CLOEXEC", which is mapped to O_NOINHERIT on Windows.
>
> Ok. So then I have no issues with it, and let's use O_CLOEXEC if it
> exists and fcntl(FD_CLOEXEC) if O_CLOEXEC doesn't exist.

I am not sure if the fallback to FD_CLOEXEC is worth doing, because
it does not exist on Windows, where leaking an open file descriptor
to a child process matters more than other platforms [*1*].  

Of course, the world is not all Windows, and there may be other
platforms this fallback may help.  But there is a chance that some
other thread may fork between the time we open(2) and the time we
call fcntl(2), leaving FD_CLOEXEC ineffective and unreliable, so
that's another thing that makes me doubt if FD_CLOEXEC fallback is
worth doing.

Having said that, here is a rewrite of [*2*] to use the fallback
would look like.  After this topic settles, we may want to also
update the tempfile.c::create_tempfile() implementation to use the
helper function we use here.


[Footnotes]

*1* The call in ce_compare_data() is to see if the contents in the
    working tree file match what is in the index.  Perhaps the
    program is "git checkout another-branch" that knows this path is
    absent in the branch being switched to and trying to make sure
    we lose no local modification.  When the path needs to be passed
    through the clean/smudge filter before hashing to see if the
    working tree contents hashes to the object in the index, we need
    to fork and then after the filter finishes its processing, we
    close the file descriptor and then unlink(2) the path if it is
    clean.  We are about to add a variant of clean/smudge mechanism
    where a lazily spawned process can clean contents of multiple
    paths and that is where cloexec starts to matter.  This function
    starts to inspect one path, opens a fd to it, finds that it
    needs filtering, spawns a long-lingering process, while leaking
    the original fd to it.  After feeding the contents via pipe and
    then receiving the filtered contents (the protocol is not full
    duplex and there won't be a stuck pipe), the parent would decide
    that the path is clean and attempts to unlink(2).  Windows does
    not let you unlink a path with open filedescriptor held, causing
    "checkout" to fail.  As it is only one fd that leaks to the
    child, no matter how many subsequent paths are filtered by the
    same long-lingering child process, it will be a far less
    important issue on other platforms that lets you unlink(2) such
    a file.

*2* http://public-inbox.org/git/xmqqh97w38gj.fsf@gitster.mtv.corp.google.com

---
 cache.h           |  1 +
 git-compat-util.h |  4 ++++
 read-cache.c      |  9 +--------
 sha1_file.c       | 47 ++++++++++++++++++++++++++++-------------------
 4 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/cache.h b/cache.h
index a902ca1f8e..6f1c21a352 100644
--- a/cache.h
+++ b/cache.h
@@ -1122,6 +1122,7 @@ extern int write_sha1_file(const void *buf, unsigned long len, const char *type,
 extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 extern int force_object_loose(const unsigned char *sha1, time_t mtime);
+extern int git_open_cloexec(const char *name, int flags);
 extern int git_open(const char *name);
 extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
diff --git a/git-compat-util.h b/git-compat-util.h
index 43718dabae..64407c701c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -683,6 +683,10 @@ char *gitstrdup(const char *s);
 #define O_CLOEXEC 0
 #endif
 
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 0
+#endif
+
 #ifdef FREAD_READS_DIRECTORIES
 #ifdef fopen
 #undef fopen
diff --git a/read-cache.c b/read-cache.c
index db5d910642..c27d3e240b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -156,14 +156,7 @@ 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 = git_open_cloexec(ce->name, O_RDONLY);
 
 	if (fd >= 0) {
 		unsigned char sha1[20];
diff --git a/sha1_file.c b/sha1_file.c
index 09045df1dc..fc8613a847 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1559,31 +1559,40 @@ 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_open_cloexec(const char *name, int flags)
 {
-	static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
+	int fd;
+	static int o_cloexec = O_CLOEXEC;
+	static int fd_cloexec = FD_CLOEXEC;
 
-	for (;;) {
-		int fd;
+	fd = open(name, flags | o_cloexec);
+	if ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
+		/* Try again w/o O_CLOEXEC: the kernel might not support it */
+		o_cloexec &= ~O_CLOEXEC;
+		fd = open(name, flags | o_cloexec);
+	}
 
-		errno = 0;
-		fd = open(name, O_RDONLY | sha1_file_open_flag);
-		if (fd >= 0)
-			return fd;
+	if (!o_cloexec && 0 <= fd && fd_cloexec) {
+		/* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */
+		int flags = fcntl(fd, F_GETFL);
+		if (fcntl(fd, F_SETFL, flags | fd_cloexec))
+			fd_cloexec = 0;
+	}
 
-		/* 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;
-		}
+	return fd;
+}
 
-		/* 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;
+int git_open(const char *name)
+{
+	static int noatime = O_NOATIME;
+	int fd = git_open_cloexec(name, O_RDONLY);
+
+	if (0 <= fd && (noatime & O_NOATIME)) {
+		int flags = fcntl(fd, F_GETFL);
+		if (fcntl(fd, F_SETFL, flags | noatime))
+			noatime = 0;
 	}
+	return fd;
 }
 
 static int stat_sha1_file(const unsigned char *sha1, struct stat *st)



^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-31 13:56                                         ` Jeff King
@ 2016-10-31 17:55                                           ` Junio C Hamano
  2016-10-31 18:05                                             ` Jeff King
  0 siblings, 1 reply; 54+ messages in thread
From: Junio C Hamano @ 2016-10-31 17:55 UTC (permalink / raw)
  To: Jeff King
  Cc: Linus Torvalds, Johannes Schindelin, Git Mailing List,
	Lars Schneider, Eric Wong

Jeff King <peff@peff.net> writes:

> If you set a probe on touch_atime() in the kernel (which is called for
> every attempt to smudge the atime, regardless of mount options, but is
> skipped when the descriptor was opened with O_NOATIME), you can see the
> impact. Here's a command I picked because it reads a lot of objects (run
> on my git.git clone):
>
>   $ perf stat -e probe:touch-atime git log -Sfoo >/dev/null
>
> And the probe:touch_atime counts before (stock git) and after (a patch
> to drop O_NOATIME):
>
>   before: 22,235
>    after: 22,362
>
> So that's only half a percent difference. And it's on a reasonably messy
> clone that is partway to triggering an auto-repack:
> ...
> So I guess it's possible that it produces a noticeable effect in some
> cases, but I'm still somewhat doubtful. And actually repacking your
> repository had a greater effect in every case I measured (in addition to
> providing other speedups).

Let's keep doubting.  I prefer one-step-at-a-time approach to
things anyway, and what I plan in the near term are:

 * use the "open() with O_NOATIME|O_CLOEXEC, gradually losing the
   bits during fallback" approach in the ls/git-open-cloexec topic,
   in order to help ls/filter-process topic be part of the upcoming
   release;

 * simplify the logic to the "open(2) with O_CLOEXEC, set O_NOATIME
   with fcntl(2)" in jc/git-open-cloexec~1 after 2.11 ships;

 * cook "drop the latter half of setting O_NOATIME" which is at the
   tip of jc/git-open-cloexec in 'next', and while Linus is looking
   the other way ^W^W^W^W^W^W^W after people had chance to complain
   with numbers, merge it to a future release iff it still looked OK
   to drop O_NOATIME thing.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC
  2016-10-31 17:55                                           ` Junio C Hamano
@ 2016-10-31 18:05                                             ` Jeff King
  0 siblings, 0 replies; 54+ messages in thread
From: Jeff King @ 2016-10-31 18:05 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Linus Torvalds, Johannes Schindelin, Git Mailing List,
	Lars Schneider, Eric Wong

On Mon, Oct 31, 2016 at 10:55:32AM -0700, Junio C Hamano wrote:

> > So I guess it's possible that it produces a noticeable effect in some
> > cases, but I'm still somewhat doubtful. And actually repacking your
> > repository had a greater effect in every case I measured (in addition to
> > providing other speedups).
> 
> Let's keep doubting.  I prefer one-step-at-a-time approach to
> things anyway, and what I plan in the near term are:
> 
>  * use the "open() with O_NOATIME|O_CLOEXEC, gradually losing the
>    bits during fallback" approach in the ls/git-open-cloexec topic,
>    in order to help ls/filter-process topic be part of the upcoming
>    release;
> 
>  * simplify the logic to the "open(2) with O_CLOEXEC, set O_NOATIME
>    with fcntl(2)" in jc/git-open-cloexec~1 after 2.11 ships;
> 
>  * cook "drop the latter half of setting O_NOATIME" which is at the
>    tip of jc/git-open-cloexec in 'next', and while Linus is looking
>    the other way ^W^W^W^W^W^W^W after people had chance to complain
>    with numbers, merge it to a future release iff it still looked OK
>    to drop O_NOATIME thing.

Great, that sounds like a good way to proceed (and if the final step
never happens, no big deal).

-Peff

^ permalink raw reply	[flat|nested] 54+ messages in thread

end of thread, other threads:[~2016-10-31 18:05 UTC | newest]

Thread overview: 54+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-24 18:02 [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks larsxschneider
2016-10-24 18:02 ` [PATCH v2 1/2] sha1_file: open window into packfiles with CLOEXEC larsxschneider
2016-10-25 10:27   ` Johannes Schindelin
2016-10-25 16:58     ` Junio C Hamano
2016-10-24 18:03 ` [PATCH v2 2/2] read-cache: make sure file handles are not inherited by child processes larsxschneider
2016-10-24 18:39   ` Eric Wong
2016-10-24 19:53     ` Junio C Hamano
2016-10-25 10:33       ` Johannes Schindelin
2016-10-25 17:02         ` Junio C Hamano
2016-10-24 19:22   ` Johannes Sixt
2016-10-24 19:53     ` Lars Schneider
2016-10-25 21:39       ` Johannes Sixt
2016-10-24 18:23 ` [PATCH v2 0/2] Use CLOEXEC to avoid fd leaks Junio C Hamano
2016-10-25 11:27 ` Johannes Schindelin
2016-10-25 18:16   ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Junio C Hamano
2016-10-25 18:16     ` [PATCH v3 1/3] sha1_file: rename git_open_noatime() to git_open() Junio C Hamano
2016-10-25 18:16     ` [PATCH v3 2/3] sha1_file: open window into packfiles with O_CLOEXEC Junio C Hamano
2016-10-26  4:25       ` Jeff King
2016-10-26 16:23         ` Junio C Hamano
2016-10-26 16:47           ` Jeff King
2016-10-26 17:52             ` Junio C Hamano
2016-10-26 20:17               ` Jeff King
2016-10-26 21:15                 ` Junio C Hamano
2016-10-27 10:24                   ` Jeff King
2016-10-27 21:49                     ` Junio C Hamano
2016-10-27 22:38                     ` Linus Torvalds
2016-10-27 22:56                       ` Junio C Hamano
2016-10-27 23:09                         ` Linus Torvalds
2016-10-27 23:19                           ` Linus Torvalds
2016-10-27 23:36                             ` Junio C Hamano
2016-10-27 23:44                               ` Linus Torvalds
2016-10-28  1:08                                 ` Junio C Hamano
2016-10-28  2:37                                   ` Junio C Hamano
2016-10-28  5:51                                     ` Eric Wong
2016-10-28 11:11                                     ` Johannes Schindelin
2016-10-28 16:13                                       ` Linus Torvalds
2016-10-28 16:48                                         ` Junio C Hamano
2016-10-28 17:38                                           ` Linus Torvalds
2016-10-28 17:47                                             ` Junio C Hamano
2016-10-29  1:26                                             ` Junio C Hamano
2016-10-29  8:25                                               ` Johannes Schindelin
2016-10-29 17:06                                                 ` Linus Torvalds
2016-10-31 17:37                                                   ` Junio C Hamano
2016-10-31 13:56                                         ` Jeff King
2016-10-31 17:55                                           ` Junio C Hamano
2016-10-31 18:05                                             ` Jeff King
2016-10-28 13:32                                     ` Junio C Hamano
2016-10-28 13:33                                       ` Junio C Hamano
2016-10-28  7:51                       ` Jeff King
2016-10-25 18:16     ` [PATCH v3 3/3] read-cache: make sure file handles are not inherited by child processes Junio C Hamano
2016-10-25 21:33       ` Eric Wong
2016-10-25 22:54         ` Junio C Hamano
2016-10-25 21:48     ` [PATCH v3 0/3] quick reroll of Lars's git_open() w/ O_CLOEXEC Lars Schneider
2016-10-25 22:56       ` Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.