From 03b6aeb27443f117a3d8375f01bc38baeeff65a5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 17 Apr 2010 13:07:34 -0700 Subject: http.c: Remove bad free of static block The filename variable here is pointing to a block of memory that was allocated by sha1_file.c and is also held in a static variable scoped within the sha1_pack_name() function. Doing a free() here is returning that memory to the allocator while we might still try to reuse it on a subsequent sha1_pack_name() invocation. That's not acceptable, so don't free it. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http.c | 1 - 1 file changed, 1 deletion(-) (limited to 'http.c') diff --git a/http.c b/http.c index deab59551..9526491a4 100644 --- a/http.c +++ b/http.c @@ -1082,7 +1082,6 @@ struct http_pack_request *new_http_pack_request( return preq; abort: - free(filename); free(preq->url); free(preq); return NULL; -- cgit v1.2.1 From 021ab6f00b66d0d3931310e77383239a606c96c2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 17 Apr 2010 13:07:36 -0700 Subject: http.c: Tiny refactoring of finish_http_pack_request Always remove the struct packed_git from the active list, even if the rename of the temporary file fails. While we are here, simplify the code a bit by using a common local variable name ("p") to hold the relevant packed_git. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'http.c') diff --git a/http.c b/http.c index 9526491a4..e9e226956 100644 --- a/http.c +++ b/http.c @@ -1002,8 +1002,9 @@ int finish_http_pack_request(struct http_pack_request *preq) { int ret; struct packed_git **lst; + struct packed_git *p = preq->target; - preq->target->pack_size = ftell(preq->packfile); + p->pack_size = ftell(preq->packfile); if (preq->packfile != NULL) { fclose(preq->packfile); @@ -1011,18 +1012,17 @@ int finish_http_pack_request(struct http_pack_request *preq) preq->slot->local = NULL; } - ret = move_temp_to_file(preq->tmpfile, preq->filename); - if (ret) - return ret; - lst = preq->lst; - while (*lst != preq->target) + while (*lst != p) lst = &((*lst)->next); *lst = (*lst)->next; - if (verify_pack(preq->target)) + ret = move_temp_to_file(preq->tmpfile, preq->filename); + if (ret) + return ret; + if (verify_pack(p)) return -1; - install_packed_git(preq->target); + install_packed_git(p); return 0; } -- cgit v1.2.1 From 3065274c58a4f4d0c6eef7e29a1484cf2c288131 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 17 Apr 2010 13:07:37 -0700 Subject: http.c: Drop useless != NULL test in finish_http_pack_request The test preq->packfile != NULL is always true. If packfile was actually NULL when entering this function the ftell() above would crash out with a SIGSEGV, resulting in never reaching this point. Simplify the code by just removing the conditional. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'http.c') diff --git a/http.c b/http.c index e9e226956..7942eea5d 100644 --- a/http.c +++ b/http.c @@ -1005,12 +1005,9 @@ int finish_http_pack_request(struct http_pack_request *preq) struct packed_git *p = preq->target; p->pack_size = ftell(preq->packfile); - - if (preq->packfile != NULL) { - fclose(preq->packfile); - preq->packfile = NULL; - preq->slot->local = NULL; - } + fclose(preq->packfile); + preq->packfile = NULL; + preq->slot->local = NULL; lst = preq->lst; while (*lst != p) -- cgit v1.2.1 From 0da8b2e7c80a6dd9743e5233cdc5acd836c9a8d3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 17 Apr 2010 13:07:38 -0700 Subject: http.c: Don't store destination name in request structures The destination name within the object store is easily computed on demand, reusing a static buffer held by sha1_file.c. We don't need to copy the entire path into the request structure for safe keeping, when it can be easily reformatted after the download has been completed. This reduces the size of the per-request structure, and removes yet another PATH_MAX based limit. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'http.c') diff --git a/http.c b/http.c index 7942eea5d..7ee1ba5a0 100644 --- a/http.c +++ b/http.c @@ -1014,7 +1014,7 @@ int finish_http_pack_request(struct http_pack_request *preq) lst = &((*lst)->next); *lst = (*lst)->next; - ret = move_temp_to_file(preq->tmpfile, preq->filename); + ret = move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1)); if (ret) return ret; if (verify_pack(p)) @@ -1043,7 +1043,6 @@ struct http_pack_request *new_http_pack_request( preq->url = strbuf_detach(&buf, NULL); filename = sha1_pack_name(target->sha1); - snprintf(preq->filename, sizeof(preq->filename), "%s", filename); snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename); preq->packfile = fopen(preq->tmpfile, "a"); if (!preq->packfile) { @@ -1133,7 +1132,6 @@ struct http_object_request *new_http_object_request(const char *base_url, freq->localfile = -1; filename = sha1_file_name(sha1); - snprintf(freq->filename, sizeof(freq->filename), "%s", filename); snprintf(freq->tmpfile, sizeof(freq->tmpfile), "%s.temp", filename); @@ -1162,8 +1160,8 @@ struct http_object_request *new_http_object_request(const char *base_url, } if (freq->localfile < 0) { - error("Couldn't create temporary file %s for %s: %s", - freq->tmpfile, freq->filename, strerror(errno)); + error("Couldn't create temporary file %s: %s", + freq->tmpfile, strerror(errno)); goto abort; } @@ -1210,8 +1208,8 @@ struct http_object_request *new_http_object_request(const char *base_url, prev_posn = 0; lseek(freq->localfile, 0, SEEK_SET); if (ftruncate(freq->localfile, 0) < 0) { - error("Couldn't truncate temporary file %s for %s: %s", - freq->tmpfile, freq->filename, strerror(errno)); + error("Couldn't truncate temporary file %s: %s", + freq->tmpfile, strerror(errno)); goto abort; } } @@ -1287,7 +1285,7 @@ int finish_http_object_request(struct http_object_request *freq) return -1; } freq->rename = - move_temp_to_file(freq->tmpfile, freq->filename); + move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1)); return freq->rename; } -- cgit v1.2.1 From 162eb5f838630f75f78f26d28b46b02781724b7d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 19 Apr 2010 07:23:05 -0700 Subject: http.c: Remove unnecessary strdup of sha1_to_hex result Most of the time the dumb HTTP transport is run without the verbose flag set, so we only need the result of sha1_to_hex(sha1) once, to construct the pack URL. Don't bother with an unnecessary malloc, copy, free chain of this buffer. If verbose is set, we'll format the SHA-1 twice now. But this tiny extra CPU time spent is nothing compared to the slowdown that is usually imposed by the verbose messages being sent to the tty, and is entirely trivial compared to the latency involved with the remote HTTP server sending something as big as a pack file. Signed-off-by: Shawn O. Pearce Acked-by: Tay Ray Chuan Signed-off-by: Junio C Hamano --- http.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'http.c') diff --git a/http.c b/http.c index 7ee1ba5a0..95e3b8bec 100644 --- a/http.c +++ b/http.c @@ -899,7 +899,6 @@ int http_fetch_ref(const char *base, struct ref *ref) static int fetch_pack_index(unsigned char *sha1, const char *base_url) { int ret = 0; - char *hex = xstrdup(sha1_to_hex(sha1)); char *filename; char *url = NULL; struct strbuf buf = STRBUF_INIT; @@ -910,10 +909,10 @@ static int fetch_pack_index(unsigned char *sha1, const char *base_url) } if (http_is_verbose) - fprintf(stderr, "Getting index for pack %s\n", hex); + fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1)); end_url_with_slash(&buf, base_url); - strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex); + strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1)); url = strbuf_detach(&buf, NULL); filename = sha1_pack_index_name(sha1); @@ -921,7 +920,6 @@ static int fetch_pack_index(unsigned char *sha1, const char *base_url) ret = error("Unable to get pack index %s\n", url); cleanup: - free(hex); free(url); return ret; } -- cgit v1.2.1 From 7b64469a363c10e9e875a73d32a220ba48694a28 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 19 Apr 2010 07:23:08 -0700 Subject: Allow parse_pack_index on temporary files The easiest way to verify a pack index is to open it through the standard parse_pack_index function, permitting the header check to happen when the file is mapped. However, the dumb HTTP client needs to verify a pack index before its moved into its proper file name within the objects/pack directory, to prevent a corrupt index from being made available. So permit the caller to specify the exact path of the index file. For now we're still using the final destination name within the sole call site in http.c, but eventually we will start to parse the temporary path instead. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'http.c') diff --git a/http.c b/http.c index 95e3b8bec..9c6263220 100644 --- a/http.c +++ b/http.c @@ -932,7 +932,7 @@ static int fetch_and_setup_pack_index(struct packed_git **packs_head, if (fetch_pack_index(sha1, base_url)) return -1; - new_pack = parse_pack_index(sha1); + new_pack = parse_pack_index(sha1, sha1_pack_index_name(sha1)); if (!new_pack) return -1; /* parse_pack_index() already issued error message */ new_pack->next = *packs_head; -- cgit v1.2.1 From fe72d420ab4dda593dddece7b907ee7868ced127 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 19 Apr 2010 07:23:09 -0700 Subject: http-fetch: Use index-pack rather than verify-pack to check packs To ensure we don't leave a corrupt pack file positioned as though it were a valid pack file, run index-pack on the temporary pack before we rename it to its final name. If index-pack crashes out when it discovers file corruption (e.g. GitHub's error HTML at the end of the file), simply delete the temporary files to cleanup. By waiting until the pack has been validated before we move it to its final name, we eliminate a race condition where another concurrent reader might try to access the pack at the same time that we are still trying to verify its not corrupt. Switching from verify-pack to index-pack is a change in behavior, but it should turn out better for users. The index-pack algorithm tries to minimize disk seeks, as well as the number of times any given object is inflated, by organizing its work along delta chains. The verify-pack logic does not attempt to do this, thrashing the delta base cache and the filesystem cache. By recreating the index file locally, we also can automatically upgrade from a v1 pack table of contents to v2. This makes the CRC32 data available for use during later repacks, even if the server didn't have them on hand. Signed-off-by: Shawn O. Pearce Acked-by: Tay Ray Chuan Signed-off-by: Junio C Hamano --- http.c | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) (limited to 'http.c') diff --git a/http.c b/http.c index 9c6263220..2ebd67939 100644 --- a/http.c +++ b/http.c @@ -1,6 +1,7 @@ #include "http.h" #include "pack.h" #include "sideband.h" +#include "run-command.h" int data_received; int active_requests; @@ -998,11 +999,14 @@ void release_http_pack_request(struct http_pack_request *preq) int finish_http_pack_request(struct http_pack_request *preq) { - int ret; struct packed_git **lst; struct packed_git *p = preq->target; + char *tmp_idx; + struct child_process ip; + const char *ip_argv[8]; + + close_pack_index(p); - p->pack_size = ftell(preq->packfile); fclose(preq->packfile); preq->packfile = NULL; preq->slot->local = NULL; @@ -1012,13 +1016,39 @@ int finish_http_pack_request(struct http_pack_request *preq) lst = &((*lst)->next); *lst = (*lst)->next; - ret = move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1)); - if (ret) - return ret; - if (verify_pack(p)) + tmp_idx = xstrdup(preq->tmpfile); + strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"), + ".idx.temp"); + + ip_argv[0] = "index-pack"; + ip_argv[1] = "-o"; + ip_argv[2] = tmp_idx; + ip_argv[3] = preq->tmpfile; + ip_argv[4] = NULL; + + memset(&ip, 0, sizeof(ip)); + ip.argv = ip_argv; + ip.git_cmd = 1; + ip.no_stdin = 1; + ip.no_stdout = 1; + + if (run_command(&ip)) { + unlink(preq->tmpfile); + unlink(tmp_idx); + free(tmp_idx); return -1; - install_packed_git(p); + } + + unlink(sha1_pack_index_name(p->sha1)); + if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1)) + || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) { + free(tmp_idx); + return -1; + } + + install_packed_git(p); + free(tmp_idx); return 0; } -- cgit v1.2.1 From 750ef42516bb343a7755f003720e43cd8dd64c3e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 19 Apr 2010 07:23:10 -0700 Subject: http-fetch: Use temporary files for pack-*.idx until verified Verify that a downloaded pack-*.idx file is consistent and valid as an index file before we rename it into its final destination. This prevents a corrupt index file from later being treated as a usable file, confusing readers. Check that we do not have the pack index file before invoking fetch_pack_index(); that way, we can do without the has_pack_index() check in fetch_pack_index(). Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http.c | 56 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 17 deletions(-) (limited to 'http.c') diff --git a/http.c b/http.c index 2ebd67939..0813c9ecd 100644 --- a/http.c +++ b/http.c @@ -897,18 +897,11 @@ int http_fetch_ref(const char *base, struct ref *ref) } /* Helpers for fetching packs */ -static int fetch_pack_index(unsigned char *sha1, const char *base_url) +static char *fetch_pack_index(unsigned char *sha1, const char *base_url) { - int ret = 0; - char *filename; - char *url = NULL; + char *url, *tmp; struct strbuf buf = STRBUF_INIT; - if (has_pack_index(sha1)) { - ret = 0; - goto cleanup; - } - if (http_is_verbose) fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1)); @@ -916,26 +909,55 @@ static int fetch_pack_index(unsigned char *sha1, const char *base_url) strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1)); url = strbuf_detach(&buf, NULL); - filename = sha1_pack_index_name(sha1); - if (http_get_file(url, filename, 0) != HTTP_OK) - ret = error("Unable to get pack index %s\n", url); + strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1)); + tmp = strbuf_detach(&buf, NULL); + + if (http_get_file(url, tmp, 0) != HTTP_OK) { + error("Unable to get pack index %s\n", url); + free(tmp); + tmp = NULL; + } -cleanup: free(url); - return ret; + return tmp; } static int fetch_and_setup_pack_index(struct packed_git **packs_head, unsigned char *sha1, const char *base_url) { struct packed_git *new_pack; + char *tmp_idx = NULL; + int ret; + + if (has_pack_index(sha1)) { + new_pack = parse_pack_index(sha1, NULL); + if (!new_pack) + return -1; /* parse_pack_index() already issued error message */ + goto add_pack; + } - if (fetch_pack_index(sha1, base_url)) + tmp_idx = fetch_pack_index(sha1, base_url); + if (!tmp_idx) return -1; - new_pack = parse_pack_index(sha1, sha1_pack_index_name(sha1)); - if (!new_pack) + new_pack = parse_pack_index(sha1, tmp_idx); + if (!new_pack) { + unlink(tmp_idx); + free(tmp_idx); + return -1; /* parse_pack_index() already issued error message */ + } + + ret = verify_pack_index(new_pack); + if (!ret) { + close_pack_index(new_pack); + ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1)); + } + free(tmp_idx); + if (ret) + return -1; + +add_pack: new_pack->next = *packs_head; *packs_head = new_pack; return 0; -- cgit v1.2.1 From 90d05713575ea6ed21d05228bcda8461f7b28ccf Mon Sep 17 00:00:00 2001 From: Tay Ray Chuan Date: Mon, 19 Apr 2010 22:46:43 +0800 Subject: http.c::new_http_pack_request: do away with the temp variable filename Now that the temporary variable char *filename is only used in one place, do away with it and just call sha1_pack_name() directly. Signed-off-by: Tay Ray Chuan Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- http.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'http.c') diff --git a/http.c b/http.c index 0813c9ecd..e41fcc380 100644 --- a/http.c +++ b/http.c @@ -1077,7 +1077,6 @@ int finish_http_pack_request(struct http_pack_request *preq) struct http_pack_request *new_http_pack_request( struct packed_git *target, const char *base_url) { - char *filename; long prev_posn = 0; char range[RANGE_HEADER_SIZE]; struct strbuf buf = STRBUF_INIT; @@ -1092,8 +1091,8 @@ struct http_pack_request *new_http_pack_request( sha1_to_hex(target->sha1)); preq->url = strbuf_detach(&buf, NULL); - filename = sha1_pack_name(target->sha1); - snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename); + snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", + sha1_pack_name(target->sha1)); preq->packfile = fopen(preq->tmpfile, "a"); if (!preq->packfile) { error("Unable to open local file %s for pack", -- cgit v1.2.1