* [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT
@ 2014-08-22 14:01 Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 1/4] convert: Refactor would_convert_to_git() to single arg 'path' Steffen Prohaska
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Steffen Prohaska @ 2014-08-22 14:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, peff, pclouds, john, schacon, Steffen Prohaska
Only changes since PATCH v3: Use ssize_t to store limits.
Steffen Prohaska (4):
convert: Refactor would_convert_to_git() to single arg 'path'
Introduce GIT_MMAP_LIMIT to allow testing expected mmap size
Change GIT_ALLOC_LIMIT check to use ssize_t for consistency
convert: Stream from fd to required clean filter instead of mmap
convert.c | 60 +++++++++++++++++++++++++++++++++++++++++++++------
convert.h | 10 ++++++---
sha1_file.c | 46 ++++++++++++++++++++++++++++++++++++---
t/t0021-conversion.sh | 24 ++++++++++++++++-----
wrapper.c | 8 +++----
5 files changed, 127 insertions(+), 21 deletions(-)
--
2.1.0.6.gb452461
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v4 1/4] convert: Refactor would_convert_to_git() to single arg 'path'
2014-08-22 14:01 [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Steffen Prohaska
@ 2014-08-22 14:01 ` Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 2/4] Introduce GIT_MMAP_LIMIT to allow testing expected mmap size Steffen Prohaska
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Steffen Prohaska @ 2014-08-22 14:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, peff, pclouds, john, schacon, Steffen Prohaska
It is only the path that matters in the decision whether to filter or
not. Clarify this by making path the single argument of
would_convert_to_git().
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
convert.h | 5 ++---
sha1_file.c | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/convert.h b/convert.h
index 0c2143c..c638b33 100644
--- a/convert.h
+++ b/convert.h
@@ -40,10 +40,9 @@ extern int convert_to_working_tree(const char *path, const char *src,
size_t len, struct strbuf *dst);
extern int renormalize_buffer(const char *path, const char *src, size_t len,
struct strbuf *dst);
-static inline int would_convert_to_git(const char *path, const char *src,
- size_t len, enum safe_crlf checksafe)
+static inline int would_convert_to_git(const char *path)
{
- return convert_to_git(path, src, len, NULL, checksafe);
+ return convert_to_git(path, NULL, 0, NULL, 0);
}
/*****************************************************************
diff --git a/sha1_file.c b/sha1_file.c
index 3f70b1d..00c07f2 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -3144,7 +3144,7 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st,
if (!S_ISREG(st->st_mode))
ret = index_pipe(sha1, fd, type, path, flags);
else if (size <= big_file_threshold || type != OBJ_BLOB ||
- (path && would_convert_to_git(path, NULL, 0, 0)))
+ (path && would_convert_to_git(path)))
ret = index_core(sha1, fd, size, type, path, flags);
else
ret = index_stream(sha1, fd, size, type, path, flags);
--
2.1.0.6.gb452461
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 2/4] Introduce GIT_MMAP_LIMIT to allow testing expected mmap size
2014-08-22 14:01 [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 1/4] convert: Refactor would_convert_to_git() to single arg 'path' Steffen Prohaska
@ 2014-08-22 14:01 ` Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 3/4] Change GIT_ALLOC_LIMIT check to use ssize_t for consistency Steffen Prohaska
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Steffen Prohaska @ 2014-08-22 14:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, peff, pclouds, john, schacon, Steffen Prohaska
Similar to testing expectations about malloc with GIT_ALLOC_LIMIT (see
commit d41489), it can be useful to test expectations about mmap.
This introduces a new environment variable GIT_MMAP_LIMIT to limit the
largest allowed mmap length (in KB). xmmap() is modified to check the
limit. Together with GIT_ALLOC_LIMIT tests can now easily confirm
expectations about memory consumption.
GIT_MMAP_LIMIT will be used in the next commit to test that data will be
streamed to an external filter without mmaping the entire file.
[commit d41489]: d41489a6424308dc9a0409bc2f6845aa08bd4f7d Add more large
blob test cases
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
sha1_file.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/sha1_file.c b/sha1_file.c
index 00c07f2..603673b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -663,10 +663,25 @@ void release_pack_memory(size_t need)
; /* nothing */
}
+static void mmap_limit_check(size_t length)
+{
+ static ssize_t limit = -1;
+ if (limit == -1) {
+ const char *env = getenv("GIT_MMAP_LIMIT");
+ limit = env ? atol(env) * 1024 : 0;
+ }
+ if (limit && length > limit)
+ die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX,
+ (uintmax_t)length, (uintmax_t)limit);
+}
+
void *xmmap(void *start, size_t length,
int prot, int flags, int fd, off_t offset)
{
- void *ret = mmap(start, length, prot, flags, fd, offset);
+ void *ret;
+
+ mmap_limit_check(length);
+ ret = mmap(start, length, prot, flags, fd, offset);
if (ret == MAP_FAILED) {
if (!length)
return NULL;
--
2.1.0.6.gb452461
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 3/4] Change GIT_ALLOC_LIMIT check to use ssize_t for consistency
2014-08-22 14:01 [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 1/4] convert: Refactor would_convert_to_git() to single arg 'path' Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 2/4] Introduce GIT_MMAP_LIMIT to allow testing expected mmap size Steffen Prohaska
@ 2014-08-22 14:01 ` Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 4/4] convert: Stream from fd to required clean filter instead of mmap Steffen Prohaska
2014-08-22 18:17 ` [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Junio C Hamano
4 siblings, 0 replies; 6+ messages in thread
From: Steffen Prohaska @ 2014-08-22 14:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, peff, pclouds, john, schacon, Steffen Prohaska
GIT_ALLOC_LIMIT limits xmalloc()'s size, which is of type size_t. Use
ssize_t to store the limit for consistency. The change has no direct
practical impact, because we use GIT_ALLOC_LIMIT to test small sizes.
The cast of size in the call to die() is changed to uintmax_t to match
the format string PRIuMAX.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
wrapper.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/wrapper.c b/wrapper.c
index bc1bfb8..e91f6e9 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -11,14 +11,14 @@ static void (*try_to_free_routine)(size_t size) = do_nothing;
static void memory_limit_check(size_t size)
{
- static int limit = -1;
+ static ssize_t limit = -1;
if (limit == -1) {
const char *env = getenv("GIT_ALLOC_LIMIT");
- limit = env ? atoi(env) * 1024 : 0;
+ limit = env ? atol(env) * 1024 : 0;
}
if (limit && size > limit)
- die("attempting to allocate %"PRIuMAX" over limit %d",
- (intmax_t)size, limit);
+ die("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX,
+ (uintmax_t)size, (uintmax_t)limit);
}
try_to_free_t set_try_to_free_routine(try_to_free_t routine)
--
2.1.0.6.gb452461
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v4 4/4] convert: Stream from fd to required clean filter instead of mmap
2014-08-22 14:01 [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Steffen Prohaska
` (2 preceding siblings ...)
2014-08-22 14:01 ` [PATCH v4 3/4] Change GIT_ALLOC_LIMIT check to use ssize_t for consistency Steffen Prohaska
@ 2014-08-22 14:01 ` Steffen Prohaska
2014-08-22 18:17 ` [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Junio C Hamano
4 siblings, 0 replies; 6+ messages in thread
From: Steffen Prohaska @ 2014-08-22 14:01 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, peff, pclouds, john, schacon, Steffen Prohaska
The data is streamed to the filter process anyway. Better avoid mapping
the file if possible. This is especially useful if a clean filter
reduces the size, for example if it computes a sha1 for binary data,
like git media. The file size that the previous implementation could
handle was limited by the available address space; large files for
example could not be handled with (32-bit) msysgit. The new
implementation can filter files of any size as long as the filter output
is small enough.
The new code path is only taken if the filter is required. The filter
consumes data directly from the fd. The original data is not available
to git, so it must fail if the filter fails.
The environment variable GIT_MMAP_LIMIT, which has been introduced in
the previous commit is used to test that the expected code path is
taken. A related test that exercises required filters is modified to
verify that the data actually has been modified on its way from the file
system to the object store.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
---
convert.c | 60 +++++++++++++++++++++++++++++++++++++++++++++------
convert.h | 5 +++++
sha1_file.c | 27 ++++++++++++++++++++++-
t/t0021-conversion.sh | 24 ++++++++++++++++-----
4 files changed, 104 insertions(+), 12 deletions(-)
diff --git a/convert.c b/convert.c
index cb5fbb4..463f6de 100644
--- a/convert.c
+++ b/convert.c
@@ -312,11 +312,12 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
struct filter_params {
const char *src;
unsigned long size;
+ int fd;
const char *cmd;
const char *path;
};
-static int filter_buffer(int in, int out, void *data)
+static int filter_buffer_or_fd(int in, int out, void *data)
{
/*
* Spawn cmd and feed the buffer contents through its stdin.
@@ -325,6 +326,7 @@ static int filter_buffer(int in, int out, void *data)
struct filter_params *params = (struct filter_params *)data;
int write_err, status;
const char *argv[] = { NULL, NULL };
+ int fd;
/* apply % substitution to cmd */
struct strbuf cmd = STRBUF_INIT;
@@ -355,7 +357,17 @@ static int filter_buffer(int in, int out, void *data)
sigchain_push(SIGPIPE, SIG_IGN);
- write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+ if (params->src) {
+ write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+ } else {
+ /* dup(), because copy_fd() closes the input fd. */
+ fd = dup(params->fd);
+ if (fd < 0)
+ write_err = error("failed to dup file descriptor.");
+ else
+ write_err = copy_fd(fd, child_process.in);
+ }
+
if (close(child_process.in))
write_err = 1;
if (write_err)
@@ -371,7 +383,7 @@ static int filter_buffer(int in, int out, void *data)
return (write_err || status);
}
-static int apply_filter(const char *path, const char *src, size_t len,
+static int apply_filter(const char *path, const char *src, size_t len, int fd,
struct strbuf *dst, const char *cmd)
{
/*
@@ -392,11 +404,12 @@ static int apply_filter(const char *path, const char *src, size_t len,
return 1;
memset(&async, 0, sizeof(async));
- async.proc = filter_buffer;
+ async.proc = filter_buffer_or_fd;
async.data = ¶ms;
async.out = -1;
params.src = src;
params.size = len;
+ params.fd = fd;
params.cmd = cmd;
params.path = path;
@@ -747,6 +760,24 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
}
}
+int would_convert_to_git_filter_fd(const char *path)
+{
+ struct conv_attrs ca;
+
+ convert_attrs(&ca, path);
+ if (!ca.drv)
+ return 0;
+
+ /* Apply a filter to an fd only if the filter is required to succeed.
+ * We must die if the filter fails, because the original data before
+ * filtering is not available.
+ */
+ if (!ca.drv->required)
+ return 0;
+
+ return apply_filter(path, NULL, 0, -1, NULL, ca.drv->clean);
+}
+
int convert_to_git(const char *path, const char *src, size_t len,
struct strbuf *dst, enum safe_crlf checksafe)
{
@@ -761,7 +792,7 @@ int convert_to_git(const char *path, const char *src, size_t len,
required = ca.drv->required;
}
- ret |= apply_filter(path, src, len, dst, filter);
+ ret |= apply_filter(path, src, len, -1, dst, filter);
if (!ret && required)
die("%s: clean filter '%s' failed", path, ca.drv->name);
@@ -778,6 +809,23 @@ int convert_to_git(const char *path, const char *src, size_t len,
return ret | ident_to_git(path, src, len, dst, ca.ident);
}
+void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst,
+ enum safe_crlf checksafe)
+{
+ struct conv_attrs ca;
+ convert_attrs(&ca, path);
+
+ assert(ca.drv);
+ assert(ca.drv->clean);
+
+ if (!apply_filter(path, NULL, 0, fd, dst, ca.drv->clean))
+ die("%s: clean filter '%s' failed", path, ca.drv->name);
+
+ ca.crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
+ crlf_to_git(path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
+ ident_to_git(path, dst->buf, dst->len, dst, ca.ident);
+}
+
static int convert_to_working_tree_internal(const char *path, const char *src,
size_t len, struct strbuf *dst,
int normalizing)
@@ -811,7 +859,7 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
}
}
- ret_filter = apply_filter(path, src, len, dst, filter);
+ ret_filter = apply_filter(path, src, len, -1, dst, filter);
if (!ret_filter && required)
die("%s: smudge filter %s failed", path, ca.drv->name);
diff --git a/convert.h b/convert.h
index c638b33..d9d853c 100644
--- a/convert.h
+++ b/convert.h
@@ -44,6 +44,11 @@ static inline int would_convert_to_git(const char *path)
{
return convert_to_git(path, NULL, 0, NULL, 0);
}
+/* Precondition: would_convert_to_git_filter_fd(path) == true */
+extern void convert_to_git_filter_fd(const char *path, int fd,
+ struct strbuf *dst,
+ enum safe_crlf checksafe);
+extern int would_convert_to_git_filter_fd(const char *path);
/*****************************************************************
*
diff --git a/sha1_file.c b/sha1_file.c
index 603673b..1274290 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -3089,6 +3089,29 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
return ret;
}
+static int index_stream_convert_blob(unsigned char *sha1, int fd,
+ const char *path, unsigned flags)
+{
+ int ret;
+ const int write_object = flags & HASH_WRITE_OBJECT;
+ struct strbuf sbuf = STRBUF_INIT;
+
+ assert(path);
+ assert(would_convert_to_git_filter_fd(path));
+
+ convert_to_git_filter_fd(path, fd, &sbuf,
+ write_object ? safe_crlf : SAFE_CRLF_FALSE);
+
+ if (write_object)
+ ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
+ sha1);
+ else
+ ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
+ sha1);
+ strbuf_release(&sbuf);
+ return ret;
+}
+
static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
const char *path, unsigned flags)
{
@@ -3156,7 +3179,9 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st,
int ret;
size_t size = xsize_t(st->st_size);
- if (!S_ISREG(st->st_mode))
+ if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
+ ret = index_stream_convert_blob(sha1, fd, path, flags);
+ else if (!S_ISREG(st->st_mode))
ret = index_pipe(sha1, fd, type, path, flags);
else if (size <= big_file_threshold || type != OBJ_BLOB ||
(path && would_convert_to_git(path)))
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index f890c54..d566508 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -153,17 +153,23 @@ test_expect_success 'filter shell-escaped filenames' '
:
'
-test_expect_success 'required filter success' '
- git config filter.required.smudge cat &&
- git config filter.required.clean cat &&
+test_expect_success 'required filter should filter data' '
+ git config filter.required.smudge ./rot13.sh &&
+ git config filter.required.clean ./rot13.sh &&
git config filter.required.required true &&
echo "*.r filter=required" >.gitattributes &&
- echo test >test.r &&
+ cat test.o >test.r &&
git add test.r &&
+
rm -f test.r &&
- git checkout -- test.r
+ git checkout -- test.r &&
+ cmp test.o test.r &&
+
+ ./rot13.sh <test.o >expected &&
+ git cat-file blob :test.r >actual &&
+ cmp expected actual
'
test_expect_success 'required filter smudge failure' '
@@ -189,6 +195,14 @@ test_expect_success 'required filter clean failure' '
echo test >test.fc &&
test_must_fail git add test.fc
'
+test_expect_success \
+'filtering large input to small output should use little memory' '
+ git config filter.devnull.clean "cat >/dev/null" &&
+ git config filter.devnull.required true &&
+ for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
+ echo "30MB filter=devnull" >.gitattributes &&
+ GIT_MMAP_LIMIT=1000 GIT_ALLOC_LIMIT=1000 git add 30MB
+'
test_expect_success EXPENSIVE 'filter large file' '
git config filter.largefile.smudge cat &&
--
2.1.0.6.gb452461
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT
2014-08-22 14:01 [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Steffen Prohaska
` (3 preceding siblings ...)
2014-08-22 14:01 ` [PATCH v4 4/4] convert: Stream from fd to required clean filter instead of mmap Steffen Prohaska
@ 2014-08-22 18:17 ` Junio C Hamano
4 siblings, 0 replies; 6+ messages in thread
From: Junio C Hamano @ 2014-08-22 18:17 UTC (permalink / raw)
To: Steffen Prohaska; +Cc: git, peff, pclouds, john, schacon
Thanks; will replace what has been on 'pu' with this one with some
tweaks.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2014-08-22 18:18 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-08-22 14:01 [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 1/4] convert: Refactor would_convert_to_git() to single arg 'path' Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 2/4] Introduce GIT_MMAP_LIMIT to allow testing expected mmap size Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 3/4] Change GIT_ALLOC_LIMIT check to use ssize_t for consistency Steffen Prohaska
2014-08-22 14:01 ` [PATCH v4 4/4] convert: Stream from fd to required clean filter instead of mmap Steffen Prohaska
2014-08-22 18:17 ` [PATCH v4 0/4] Stream fd to clean filter, GIT_MMAP_LIMIT, GIT_ALLOC_LIMIT Junio C Hamano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).