Introduce fast_export_save_blob, meant to be used after printing cat-blob :n to the fast-import stream. It reads a response from fd 3 in cat-file --batch format. To avoid deadlock, it uses file descriptor-level calls (no stdio) and reads only one character at a time (though the latter restriction could and should be relaxed somehow --- O_NONBLOCK, perhaps). Signed-off-by: Jonathan Nieder <jrnieder@xxxxxxxxx> --- The effect of this one is almost completely undone by a later patch. :) Probably it would be better to use stdio with O_NONBLOCK, after all (David, sorry I forgot about our conversations on this before). This series uses file descriptors to be conservative, because I do not have much of a desire to test for proper O_NONBLOCK support on the relevant platforms yet. vcs-svn/fast_export.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 87 insertions(+), 0 deletions(-) diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c index 6cfa256..3feef66 100644 --- a/vcs-svn/fast_export.c +++ b/vcs-svn/fast_export.c @@ -8,8 +8,10 @@ #include "line_buffer.h" #include "repo_tree.h" #include "string_pool.h" +#include "strbuf.h" #define MAX_GITSVN_LINE_LEN 4096 +#define REPORT_FILENO 3 static uint32_t first_commit_done; @@ -63,6 +65,91 @@ void fast_export_commit(uint32_t revision, uint32_t author, char *log, printf("progress Imported commit %"PRIu32".\n\n", revision); } +static int ends_with(const char *s, size_t len, const char *suffix) +{ + const size_t suffixlen = strlen(suffix); + if (len < suffixlen) + return 0; + return !memcmp(s + len - suffixlen, suffix, suffixlen); +} + +static int parse_cat_response_line(const char *header, size_t *len) +{ + size_t headerlen = strlen(header); + const char *type; + const char *end; + + if (ends_with(header, headerlen, " missing")) + return error("cat-blob reports missing blob: %s", header); + type = memmem(header, headerlen, " blob ", strlen(" blob ")); + if (!type) + return error("cat-blob header has wrong object type: %s", header); + *len = strtoumax(type + strlen(" blob "), (char **) &end, 10); + if (end == type + strlen(" blob ")) + return error("cat-blob header does not contain length: %s", header); + if (*end) + return error("cat-blob header contains garbage after length: %s", header); + return 0; +} + +static struct strbuf response_line = STRBUF_INIT; +static const char *get_response_line(void) +{ + /* + * NEEDSWORK: Does not actually need to read one byte at a time. + * Some platforms have O_NONBLOCK. On others we could read 8 chars + * at a time until a potential appearance of " blob ". + */ + strbuf_reset(&response_line); + for (;;) { + char buf[1]; + if (xread(REPORT_FILENO, buf, 1) < 0) { + error("cannot read cat-blob result: %s", strerror(errno)); + return NULL; + } + if (*buf == '\n') + return response_line.buf; + strbuf_addch(&response_line, *buf); + } +} + +static int copy_bytes(FILE *out, size_t len) +{ + char buf[4096]; + ssize_t nread; + for (; len; len -= nread) { + nread = xread(REPORT_FILENO, buf, + len < sizeof(buf) ? len : sizeof(buf)); + if (nread < 0) + return error("cannot copy cat-blob result: %s", + strerror(errno)); + if (!nread) + return error("0-length read..."); + if (fwrite(buf, 1, nread, out) != nread) + return error("cannot write cat-blob results: %s", + strerror(errno)); + } +} + +static int fast_export_save_blob(FILE *out) +{ + size_t len = len; + const char *header, *tail; + + header = get_response_line(); + if (!header || parse_cat_response_line(header, &len)) + return -1; + copy_bytes(out, len); + + /* Discard trailing newline. */ + tail = get_response_line(); + if (!tail) + return -1; + if (*tail) + return error("line following cat-blob response contains garbage: %s", tail); + return 0; +} + void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len) { if (mode == REPO_MODE_LNK) { -- 1.7.2.3 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html