[PATCH 4/5] cli/dump: define GZPUTS and use it in notmuch-dump

2020-04-12 Thread David Bremner
Similarly to GZPRINTF, this is a drop in replacement that can be
improved where needd.
---
 notmuch-client.h |  1 +
 notmuch-dump.c   | 10 +-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 55d4d526..7efcb06f 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -499,6 +499,7 @@ print_status_gzbytes (const char *loc,
 
 #define ASSERT_GZBYTES(file, bytes) ( (print_status_gzbytes(__location__, 
file, bytes)) ?  exit(1) : 0 )
 #define GZPRINTF(file, fmt, ...) ASSERT_GZBYTES(file, gzprintf (file, fmt, 
##__VA_ARGS__));
+#define GZPUTS(file, str) ASSERT_GZBYTES(file, gzputs (file, str));
 
 #include "command-line-arguments.h"
 
diff --git a/notmuch-dump.c b/notmuch-dump.c
index ca45d885..fb322237 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -76,7 +76,7 @@ print_dump_header (gzFile output, int output_format, int 
include)
  NOTMUCH_DUMP_VERSION);
 
 if (include & DUMP_INCLUDE_CONFIG) {
-   gzputs (output, "config");
+   GZPUTS (output, "config");
sep = ",";
 }
 if (include & DUMP_INCLUDE_PROPERTIES) {
@@ -86,7 +86,7 @@ print_dump_header (gzFile output, int output_format, int 
include)
 if (include & DUMP_INCLUDE_TAGS) {
GZPRINTF (output, "%stags", sep);
 }
-gzputs (output, "\n");
+GZPUTS (output, "\n");
 }
 
 static int
@@ -174,12 +174,12 @@ dump_tags_message (void *ctx,
const char *tag_str = notmuch_tags_get (tags);
 
if (! first)
-   gzputs (output, " ");
+   GZPUTS (output, " ");
 
first = 0;
 
if (output_format == DUMP_FORMAT_SUP) {
-   gzputs (output, tag_str);
+   GZPUTS (output, tag_str);
} else {
if (hex_encode (ctx, tag_str,
buffer_p, size_p) != HEX_SUCCESS) {
@@ -192,7 +192,7 @@ dump_tags_message (void *ctx,
 }
 
 if (output_format == DUMP_FORMAT_SUP) {
-   gzputs (output, ")\n");
+   GZPUTS (output, ")\n");
 } else {
if (make_boolean_term (ctx, "id", message_id,
   buffer_p, size_p)) {
-- 
2.25.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 5/5] cli/dump: replace use of gzprintf with gzputs for config values

2020-04-12 Thread David Bremner
These can be large, and hit buffer limitations of gzprintf.
---
 notmuch-dump.c| 4 +++-
 test/T240-dump-restore.sh | 1 -
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index fb322237..23d7d20a 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -51,7 +51,9 @@ database_dump_config (notmuch_database_t *notmuch, gzFile 
output)
goto DONE;
}
 
-   GZPRINTF (output, " %s\n", buffer);
+   GZPUTS (output, " ");
+   GZPUTS (output, buffer);
+   GZPUTS (output, "\n");
 }
 
 ret = EXIT_SUCCESS;
diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh
index 374db5c2..3b6ab8fd 100755
--- a/test/T240-dump-restore.sh
+++ b/test/T240-dump-restore.sh
@@ -323,7 +323,6 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest 'dumping large queries'
-test_subtest_known_broken
 # This value repeat was found experimentally by binary search. The
 # config value after URL encoding is exactly 4096 bytes, which
 # suggests a buffer size bug.
-- 
2.25.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[no subject]

2020-04-12 Thread David Bremner
Here's a not too ambitious attempt to clean up the error handling on
zlib output. It's bit gross to treat any error reported by zlib as
fatal, but it's a step up from ignoring them, and it's in the client
code, not in the library.

The first patch splits out the first fix of
id:20200410173039.yextmxk4wtgpl...@siegel.lan

The last patch is a fix for the bug reported by qsx.


___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 1/5] util/zlib-extra.c: don't pass NULL to gzerror.

2020-04-12 Thread David Bremner
Although (as of 1.2.11) zlib checks this parameter before writing to
it, the docs don't promise to keep doing so, so be safe.
---
 util/zlib-extra.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/util/zlib-extra.c b/util/zlib-extra.c
index f691cccf..e17a9731 100644
--- a/util/zlib-extra.c
+++ b/util/zlib-extra.c
@@ -79,8 +79,10 @@ gz_getline (void *talloc_ctx, char **bufptr, ssize_t 
*bytes_read, gzFile stream)
 const char *
 gz_error_string (util_status_t status, gzFile file)
 {
+int dummy;
+
 if (status == UTIL_GZERROR)
-   return gzerror (file, NULL);
+   return gzerror (file, &dummy);
 else
return util_error_string (status);
 }
-- 
2.25.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 3/5] cli/dump: define GZPRINTF macro and use it in place of gzprintf

2020-04-12 Thread David Bremner
This will at least catch errors, and can be replaced with more
sophisticated error handling where appropriate.
---
 notmuch-client.h |  3 +++
 notmuch-dump.c   | 24 
 2 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 467e1d84..55d4d526 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -497,6 +497,9 @@ print_status_gzbytes (const char *loc,
  gzFile file,
  int bytes);
 
+#define ASSERT_GZBYTES(file, bytes) ( (print_status_gzbytes(__location__, 
file, bytes)) ?  exit(1) : 0 )
+#define GZPRINTF(file, fmt, ...) ASSERT_GZBYTES(file, gzprintf (file, fmt, 
##__VA_ARGS__));
+
 #include "command-line-arguments.h"
 
 extern const char *notmuch_requested_db_uuid;
diff --git a/notmuch-dump.c b/notmuch-dump.c
index 65e02639..ca45d885 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -42,7 +42,7 @@ database_dump_config (notmuch_database_t *notmuch, gzFile 
output)
 notmuch_config_list_key (list));
goto DONE;
}
-   gzprintf (output, "#@ %s", buffer);
+   GZPRINTF (output, "#@ %s", buffer);
 
if (hex_encode (notmuch, notmuch_config_list_value (list),
&buffer, &buffer_size) != HEX_SUCCESS) {
@@ -51,7 +51,7 @@ database_dump_config (notmuch_database_t *notmuch, gzFile 
output)
goto DONE;
}
 
-   gzprintf (output, " %s\n", buffer);
+   GZPRINTF (output, " %s\n", buffer);
 }
 
 ret = EXIT_SUCCESS;
@@ -71,7 +71,7 @@ print_dump_header (gzFile output, int output_format, int 
include)
 {
 const char *sep = "";
 
-gzprintf (output, "#notmuch-dump %s:%d ",
+GZPRINTF (output, "#notmuch-dump %s:%d ",
  (output_format == DUMP_FORMAT_SUP) ? "sup" : "batch-tag",
  NOTMUCH_DUMP_VERSION);
 
@@ -80,11 +80,11 @@ print_dump_header (gzFile output, int output_format, int 
include)
sep = ",";
 }
 if (include & DUMP_INCLUDE_PROPERTIES) {
-   gzprintf (output, "%sproperties", sep);
+   GZPRINTF (output, "%sproperties", sep);
sep = ",";
 }
 if (include & DUMP_INCLUDE_TAGS) {
-   gzprintf (output, "%stags", sep);
+   GZPRINTF (output, "%stags", sep);
 }
 gzputs (output, "\n");
 }
@@ -115,7 +115,7 @@ dump_properties_message (void *ctx,
fprintf (stderr, "Error: failed to hex-encode message-id %s\n", 
message_id);
return 1;
}
-   gzprintf (output, "#= %s", *buffer_p);
+   GZPRINTF (output, "#= %s", *buffer_p);
first = false;
}
 
@@ -126,18 +126,18 @@ dump_properties_message (void *ctx,
fprintf (stderr, "Error: failed to hex-encode key %s\n", key);
return 1;
}
-   gzprintf (output, " %s", *buffer_p);
+   GZPRINTF (output, " %s", *buffer_p);
 
if (hex_encode (ctx, val, buffer_p, size_p) != HEX_SUCCESS) {
fprintf (stderr, "Error: failed to hex-encode value %s\n", val);
return 1;
}
-   gzprintf (output, "=%s", *buffer_p);
+   GZPRINTF (output, "=%s", *buffer_p);
 }
 notmuch_message_properties_destroy (list);
 
 if (! first)
-   gzprintf (output, "\n", *buffer_p);
+   GZPRINTF (output, "\n", *buffer_p);
 
 return 0;
 }
@@ -165,7 +165,7 @@ dump_tags_message (void *ctx,
 }
 
 if (output_format == DUMP_FORMAT_SUP) {
-   gzprintf (output, "%s (", message_id);
+   GZPRINTF (output, "%s (", message_id);
 }
 
 for (notmuch_tags_t *tags = notmuch_message_get_tags (message);
@@ -187,7 +187,7 @@ dump_tags_message (void *ctx,
 tag_str);
return EXIT_FAILURE;
}
-   gzprintf (output, "+%s", *buffer_p);
+   GZPRINTF (output, "+%s", *buffer_p);
}
 }
 
@@ -200,7 +200,7 @@ dump_tags_message (void *ctx,
 message_id, strerror (errno));
return EXIT_FAILURE;
}
-   gzprintf (output, " -- %s\n", *buffer_p);
+   GZPRINTF (output, " -- %s\n", *buffer_p);
 }
 return EXIT_SUCCESS;
 }
-- 
2.25.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 2/5] status: add print_status_gzbytes

2020-04-12 Thread David Bremner
This is in the client code, rather than libnotmuch_util, because it
prints to stderr. Also it in pretends to generate notmuch status
codes.
---
 notmuch-client.h |  8 +++-
 status.c | 14 ++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index 89e15ba6..467e1d84 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -49,6 +49,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "talloc-extra.h"
 #include "crypto.h"
@@ -469,7 +470,7 @@ notmuch_database_dump (notmuch_database_t *notmuch,
   dump_include_t include,
   bool gzip_output);
 
-/* If status is non-zero (i.e. error) print appropriate
+/* If status indicates error print appropriate
  * messages to stderr.
  */
 
@@ -491,6 +492,11 @@ print_status_database (const char *loc,
 int
 status_to_exit (notmuch_status_t status);
 
+notmuch_status_t
+print_status_gzbytes (const char *loc,
+ gzFile file,
+ int bytes);
+
 #include "command-line-arguments.h"
 
 extern const char *notmuch_requested_db_uuid;
diff --git a/status.c b/status.c
index d0ae47f4..09d82a17 100644
--- a/status.c
+++ b/status.c
@@ -72,3 +72,17 @@ status_to_exit (notmuch_status_t status)
return EXIT_FAILURE;
 }
 }
+
+notmuch_status_t
+print_status_gzbytes (const char *loc, gzFile file, int bytes)
+{
+if (bytes <= 0) {
+   int errnum;
+   const char *errstr = gzerror (file, &errnum);
+   fprintf (stderr, "%s: zlib error %s (%d)\n", loc, errstr, errnum);
+   return NOTMUCH_STATUS_FILE_ERROR;
+} else {
+   return NOTMUCH_STATUS_SUCCESS;
+}
+}
+
-- 
2.25.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] test: add known_broken test for dumping large stored queries

2020-04-12 Thread David Bremner
David Bremner  writes:

> 'qsx' reported a bug on #notmuch with notmuch-dump and large stored
> queries. This test will pass (on my machine) if the value of `repeat'
> is made smaller.

I believe the underlying problem is explained by this comment in zlib.h

   The number of uncompressed bytes written is limited to 8191, or
   one less than the buffer size given to gzbuffer().  The caller should assure
   that this limit is not exceeded.  If it is exceeded, then gzprintf() will
   return an error (0) with nothing written.

So calling gzbuffer can increase this limit, but you still have to guess
before writing for the first time.

One way of interpreting this is that we should avoid using gzprintf
where we cannot predict the output size.

d
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] test: add known_broken test for dumping large stored queries

2020-04-12 Thread David Bremner
'qsx' reported a bug on #notmuch with notmuch-dump and large stored
queries. This test will pass (on my machine) if the value of `repeat'
is made smaller.
---
 test/T240-dump-restore.sh | 13 +
 1 file changed, 13 insertions(+)

diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh
index 0870ff92..374db5c2 100755
--- a/test/T240-dump-restore.sh
+++ b/test/T240-dump-restore.sh
@@ -322,6 +322,19 @@ EOF
 
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest 'dumping large queries'
+test_subtest_known_broken
+# This value repeat was found experimentally by binary search. The
+# config value after URL encoding is exactly 4096 bytes, which
+# suggests a buffer size bug.
+repeat=1329
+notmuch config set query.big "$(seq -s' ' $repeat)"
+notmuch dump --include=config > OUTPUT
+notmuch config set query.big ''
+printf "#notmuch-dump batch-tag:3 config\n#@ query.big " > EXPECTED
+seq -s'%20'  $repeat >> EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest 'roundtripping random message-ids and tags'
 
 ${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
-- 
2.25.1

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] zlib-related bugs

2020-04-12 Thread Olivier Taïbi
the following diff addresses 3 zlib-related bugs in notmuch.
1) the second argument of gzerror() cannot be NULL, so replace it by a dummy
   &errnum.
2) gzerror() cannot be closed after gzclosed(), so just print the error value
   instead.
3) in gz_getline(), if gz_error sets its second argument to Z_STREAM_END then
   there was no error (only EOF).  Unfortunately the zlib manual is not very
   clear on the meaning of Z_STREAM_END, but I don't see how it could be an
   error.  I found this issue by using notmuch on OpenBSD, which has an old
   zlib.  I encountered other issues with notmuch on OpenBSD (e.g. there is no
   transparency mode in this older zlib, so notmuch dump output is always
   gzipped), but they do not seem to be bugs in notmuch.

diff --git a/notmuch-dump.c b/notmuch-dump.c
index 65e02639..5d4b2fa7 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -287,6 +287,7 @@ notmuch_database_dump (notmuch_database_t *notmuch,
 int outfd = -1;
 
 int ret = -1;
+int errnum;
 
 if (output_file_name) {
tempname = talloc_asprintf (notmuch, "%s.XX", output_file_name);
@@ -316,7 +317,7 @@ notmuch_database_dump (notmuch_database_t *notmuch,
 
 ret = gzflush (output, Z_FINISH);
 if (ret) {
-   fprintf (stderr, "Error flushing output: %s\n", gzerror (output, NULL));
+   fprintf (stderr, "Error flushing output: %s\n", gzerror (output, 
&errnum));
goto DONE;
 }
 
@@ -332,7 +333,7 @@ notmuch_database_dump (notmuch_database_t *notmuch,
 ret = gzclose_w (output);
 if (ret) {
fprintf (stderr, "Error closing %s: %s\n", name_for_error,
-gzerror (output, NULL));
+gzerror (output, &errnum));
ret = EXIT_FAILURE;
output = NULL;
goto DONE;
diff --git a/notmuch-restore.c b/notmuch-restore.c
index 4b509d95..e2dc3d45 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -237,6 +237,7 @@ notmuch_restore_command (notmuch_config_t *config, int 
argc, char *argv[])
 int opt_index;
 int include = 0;
 int input_format = DUMP_FORMAT_AUTO;
+int errnum;
 
 if (notmuch_database_open (notmuch_config_get_database_path (config),
   NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much))
@@ -448,10 +449,13 @@ notmuch_restore_command (notmuch_config_t *config, int 
argc, char *argv[])
 if (notmuch)
notmuch_database_destroy (notmuch);
 
-if (input && gzclose_r (input)) {
-   fprintf (stderr, "Error closing %s: %s\n",
-name_for_error, gzerror (input, NULL));
-   ret = EXIT_FAILURE;
+if (input) {
+   errnum = gzclose_r (input);
+   if (errnum) {
+   fprintf (stderr, "Error closing %s: %d\n",
+name_for_error, errnum);
+   ret = EXIT_FAILURE;
+   }
 }
 
 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/util/zlib-extra.c b/util/zlib-extra.c
index f691cccf..3800fa60 100644
--- a/util/zlib-extra.c
+++ b/util/zlib-extra.c
@@ -47,6 +47,7 @@ gz_getline (void *talloc_ctx, char **bufptr, ssize_t 
*bytes_read, gzFile stream)
int zlib_status = 0;
(void) gzerror (stream, &zlib_status);
switch (zlib_status) {
+   case Z_STREAM_END:
case Z_OK:
/* no data read before EOF */
if (offset == 0)
@@ -79,8 +80,9 @@ gz_getline (void *talloc_ctx, char **bufptr, ssize_t 
*bytes_read, gzFile stream)
 const char *
 gz_error_string (util_status_t status, gzFile file)
 {
+int errnum;
 if (status == UTIL_GZERROR)
-   return gzerror (file, NULL);
+   return gzerror (file, &errnum);
 else
return util_error_string (status);
 }
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch