From bb9e15a83c425ff31e4da6b004bd4557d528b755 Mon Sep 17 00:00:00 2001 From: Uwe Zeisberger Date: Wed, 21 Jun 2006 11:04:12 +0200 Subject: [PATCH 1/5] Fix possible out-of-bounds array access If match is "", match[-1] is accessed. Let pathspec_matches return 1 in that case indicating that "" matches everything. Incidently this fixes git-grep'ing in ".". Signed-off-by: Uwe Zeisberger Signed-off-by: Junio C Hamano --- builtin-grep.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin-grep.c b/builtin-grep.c index 9806499263..f7767bb4ee 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -29,10 +29,11 @@ static int pathspec_matches(const char **paths, const char *name) int matchlen = strlen(match); const char *cp, *meta; - if ((matchlen <= namelen) && - !strncmp(name, match, matchlen) && - (match[matchlen-1] == '/' || - name[matchlen] == '\0' || name[matchlen] == '/')) + if (!matchlen || + ((matchlen <= namelen) && + !strncmp(name, match, matchlen) && + (match[matchlen-1] == '/' || + name[matchlen] == '\0' || name[matchlen] == '/'))) return 1; if (!fnmatch(match, name, 0)) return 1; From b1c71b72815cb82a8bad14020a047320b88a04eb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 20 Jun 2006 18:26:34 -0700 Subject: [PATCH 2/5] upload-pack: avoid sending an incomplete pack upon failure When the repository on the remote side is corrupted, rev-list spawned from upload-pack would die with error, but pack-objects that reads from the rev-list happily created a packfile that can be unpacked by the downloader. When this happens, the resulting packfile is not corrupted and unpacks cleanly, but the list of the objects contained in it is not what the protocol exchange computed. This update makes upload-pack to monitor its subprocesses, and when either of them dies with error, sends an incomplete pack data to the downloader to cause it to fail. Signed-off-by: Junio C Hamano --- upload-pack.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 163 insertions(+), 14 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index 979e58306e..a9a8f2ed12 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -5,6 +5,9 @@ #include "object.h" #include "commit.h" #include "exec_cmd.h" +#include +#include +#include static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] "; @@ -33,17 +36,20 @@ static int strip(char *line, int len) static void create_pack_file(void) { - int fd[2]; - pid_t pid; + /* Pipes between rev-list to pack-objects and pack-objects to us. */ + int lp_pipe[2], pu_pipe[2]; + pid_t pid_rev_list, pid_pack_objects; int create_full_pack = (nr_our_refs == nr_needs && !nr_has); + char data[8193]; + int buffered = -1; - if (pipe(fd) < 0) + if (pipe(lp_pipe) < 0) die("git-upload-pack: unable to create pipe"); - pid = fork(); - if (pid < 0) + pid_rev_list = fork(); + if (pid_rev_list < 0) die("git-upload-pack: unable to fork git-rev-list"); - if (!pid) { + if (!pid_rev_list) { int i; int args; const char **argv; @@ -60,10 +66,10 @@ static void create_pack_file(void) argv = (const char **) p; buf = xmalloc(args * 45); - dup2(fd[1], 1); + dup2(lp_pipe[1], 1); close(0); - close(fd[0]); - close(fd[1]); + close(lp_pipe[0]); + close(lp_pipe[1]); *p++ = "rev-list"; *p++ = use_thin_pack ? "--objects-edge" : "--objects"; if (create_full_pack || MAX_NEEDS <= nr_needs) @@ -86,11 +92,154 @@ static void create_pack_file(void) execv_git_cmd(argv); die("git-upload-pack: unable to exec git-rev-list"); } - dup2(fd[0], 0); - close(fd[0]); - close(fd[1]); - execl_git_cmd("pack-objects", "--stdout", NULL); - die("git-upload-pack: unable to exec git-pack-objects"); + + if (pipe(pu_pipe) < 0) + die("git-upload-pack: unable to create pipe"); + pid_pack_objects = fork(); + if (pid_pack_objects < 0) { + /* daemon sets things up to ignore TERM */ + kill(pid_rev_list, SIGKILL); + die("git-upload-pack: unable to fork git-pack-objects"); + } + if (!pid_pack_objects) { + dup2(lp_pipe[0], 0); + dup2(pu_pipe[1], 1); + + close(lp_pipe[0]); + close(lp_pipe[1]); + close(pu_pipe[0]); + close(pu_pipe[1]); + execl_git_cmd("pack-objects", "--stdout", NULL); + kill(pid_rev_list, SIGKILL); + die("git-upload-pack: unable to exec git-pack-objects"); + } + + close(lp_pipe[0]); + close(lp_pipe[1]); + + /* We read from pu_pipe[0] to capture the pack data. + */ + close(pu_pipe[1]); + + while (1) { + const char *who; + struct pollfd pfd[2]; + pid_t pid; + int status; + ssize_t sz; + int pu, pollsize; + + pollsize = 0; + pu = -1; + + if (0 <= pu_pipe[0]) { + pfd[pollsize].fd = pu_pipe[0]; + pfd[pollsize].events = POLLIN; + pu = pollsize; + pollsize++; + } + + if (pollsize) { + if (poll(pfd, pollsize, -1) < 0) { + if (errno != EINTR) { + error("poll failed, resuming: %s", + strerror(errno)); + sleep(1); + } + continue; + } + if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { + /* Data ready; we keep the last byte + * to ourselves in case we detect + * broken rev-list, so that we can + * leave the stream corrupted. This + * is unfortunate -- unpack-objects + * would happily accept a valid pack + * data with trailing garbage, so + * appending garbage after we pass all + * the pack data is not good enough to + * signal breakage to downstream. + */ + char *cp = data; + ssize_t outsz = 0; + if (0 <= buffered) { + *cp++ = buffered; + outsz++; + } + sz = read(pu_pipe[0], cp, + sizeof(data) - outsz); + if (0 < sz) + ; + else if (sz == 0) { + close(pu_pipe[0]); + pu_pipe[0] = -1; + } + else + goto fail; + sz += outsz; + if (1 < sz) { + buffered = data[sz-1] & 0xFF; + sz--; + } + else + buffered = -1; + sz = xwrite(1, data, sz); + if (sz < 0) + goto fail; + } + } + + /* See if the children are still there */ + if (pid_rev_list || pid_pack_objects) { + pid = waitpid(-1, &status, WNOHANG); + if (!pid) + continue; + who = ((pid == pid_rev_list) ? "git-rev-list" : + (pid == pid_pack_objects) ? "git-pack-objects" : + NULL); + if (!who) { + if (pid < 0) { + error("git-upload-pack: %s", + strerror(errno)); + goto fail; + } + error("git-upload-pack: we weren't " + "waiting for %d", pid); + continue; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) { + error("git-upload-pack: %s died with error.", + who); + goto fail; + } + if (pid == pid_rev_list) + pid_rev_list = 0; + if (pid == pid_pack_objects) + pid_pack_objects = 0; + if (pid_rev_list || pid_pack_objects) + continue; + } + + /* both died happily */ + if (pollsize) + continue; + + /* flush the data */ + if (0 <= buffered) { + data[0] = buffered; + sz = xwrite(1, data, 1); + if (sz < 0) + goto fail; + fprintf(stderr, "flushed.\n"); + } + return; + } + fail: + if (pid_pack_objects) + kill(pid_pack_objects, SIGKILL); + if (pid_rev_list) + kill(pid_rev_list, SIGKILL); + die("git-upload-pack: aborting due to possible repository corruption on the remote side."); } static int got_sha1(char *hex, unsigned char *sha1) From 363b7817e017b7c7e27a925d766c4d9bacfe4471 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 20 Jun 2006 22:48:23 -0700 Subject: [PATCH 3/5] upload-pack: prepare for sideband message support. This does not implement sideband for propagating the status to the downloader yet, but add code to capture the standard error output from the pack-objects process in preparation for sending it off to the client when the protocol extension allows us to do so. Signed-off-by: Junio C Hamano --- pack-objects.c | 4 ++++ upload-pack.c | 46 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/pack-objects.c b/pack-objects.c index 179560f2bd..7a8c16c317 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -1221,6 +1221,10 @@ int main(int argc, char **argv) local = 1; continue; } + if (!strcmp("--progress", arg)) { + progress = 1; + continue; + } if (!strcmp("--incremental", arg)) { incremental = 1; continue; diff --git a/upload-pack.c b/upload-pack.c index a9a8f2ed12..13eaa22780 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -36,11 +36,13 @@ static int strip(char *line, int len) static void create_pack_file(void) { - /* Pipes between rev-list to pack-objects and pack-objects to us. */ - int lp_pipe[2], pu_pipe[2]; + /* Pipes between rev-list to pack-objects, pack-objects to us + * and pack-objects error stream for progress bar. + */ + int lp_pipe[2], pu_pipe[2], pe_pipe[2]; pid_t pid_rev_list, pid_pack_objects; int create_full_pack = (nr_our_refs == nr_needs && !nr_has); - char data[8193]; + char data[8193], progress[128]; int buffered = -1; if (pipe(lp_pipe) < 0) @@ -95,6 +97,8 @@ static void create_pack_file(void) if (pipe(pu_pipe) < 0) die("git-upload-pack: unable to create pipe"); + if (pipe(pe_pipe) < 0) + die("git-upload-pack: unable to create pipe"); pid_pack_objects = fork(); if (pid_pack_objects < 0) { /* daemon sets things up to ignore TERM */ @@ -104,12 +108,15 @@ static void create_pack_file(void) if (!pid_pack_objects) { dup2(lp_pipe[0], 0); dup2(pu_pipe[1], 1); + dup2(pe_pipe[1], 2); close(lp_pipe[0]); close(lp_pipe[1]); close(pu_pipe[0]); close(pu_pipe[1]); - execl_git_cmd("pack-objects", "--stdout", NULL); + close(pe_pipe[0]); + close(pe_pipe[1]); + execl_git_cmd("pack-objects", "--stdout", "--progress", NULL); kill(pid_rev_list, SIGKILL); die("git-upload-pack: unable to exec git-pack-objects"); } @@ -117,20 +124,23 @@ static void create_pack_file(void) close(lp_pipe[0]); close(lp_pipe[1]); - /* We read from pu_pipe[0] to capture the pack data. + /* We read from pe_pipe[0] to capture stderr output for + * progress bar, and pu_pipe[0] to capture the pack data. */ + close(pe_pipe[1]); close(pu_pipe[1]); while (1) { const char *who; + char *cp; struct pollfd pfd[2]; pid_t pid; int status; ssize_t sz; - int pu, pollsize; + int pe, pu, pollsize; pollsize = 0; - pu = -1; + pe = pu = -1; if (0 <= pu_pipe[0]) { pfd[pollsize].fd = pu_pipe[0]; @@ -138,6 +148,12 @@ static void create_pack_file(void) pu = pollsize; pollsize++; } + if (0 <= pe_pipe[0]) { + pfd[pollsize].fd = pe_pipe[0]; + pfd[pollsize].events = POLLIN; + pe = pollsize; + pollsize++; + } if (pollsize) { if (poll(pfd, pollsize, -1) < 0) { @@ -187,6 +203,22 @@ static void create_pack_file(void) if (sz < 0) goto fail; } + if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { + /* Status ready; we do not use it for now, + * but later we will add side-band to send it + * to the other side. + */ + sz = read(pe_pipe[0], progress, + sizeof(progress)); + if (0 < sz) + write(2, progress, sz); + else if (sz == 0) { + close(pe_pipe[0]); + pe_pipe[0] = -1; + } + else + goto fail; + } } /* See if the children are still there */ From efc7fa5355da79326f92716eef37ddd71c7ec034 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 20 Jun 2006 23:54:26 -0700 Subject: [PATCH 4/5] Retire git-clone-pack The program is not used by git-clone since git-fetch-pack was extended to allow its caller do what git-clone-pack alone did, and git-clone was updated to use it. Signed-off-by: Junio C Hamano --- .gitignore | 1 - Documentation/git-clone-pack.txt | 64 ---------- Documentation/git-clone.txt | 4 +- Documentation/git-receive-pack.txt | 3 +- Documentation/git-upload-pack.txt | 2 +- Documentation/git.txt | 6 +- INSTALL | 2 +- Makefile | 2 +- clone-pack.c | 186 ----------------------------- 9 files changed, 7 insertions(+), 263 deletions(-) delete mode 100644 Documentation/git-clone-pack.txt delete mode 100644 clone-pack.c diff --git a/.gitignore b/.gitignore index afd0876218..65aa939f6b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ git-cherry git-cherry-pick git-clean git-clone -git-clone-pack git-commit git-commit-tree git-convert-objects diff --git a/Documentation/git-clone-pack.txt b/Documentation/git-clone-pack.txt deleted file mode 100644 index 09f43eefe4..0000000000 --- a/Documentation/git-clone-pack.txt +++ /dev/null @@ -1,64 +0,0 @@ -git-clone-pack(1) -================= - -NAME ----- -git-clone-pack - Clones a repository by receiving packed objects - - -SYNOPSIS --------- -'git-clone-pack' [--exec=] [:] [...] - -DESCRIPTION ------------ -Clones a repository into the current repository by invoking -'git-upload-pack', possibly on the remote host via ssh, in -the named repository, and stores the sent pack in the local -repository. - -OPTIONS -------- ---exec=:: - Use this to specify the path to 'git-upload-pack' on the - remote side, if it is not found on your $PATH. - Installations of sshd ignore the user's environment - setup scripts for login shells (e.g. .bash_profile) and - your privately installed git may not be found on the system - default $PATH. Another workaround suggested is to set - up your $PATH in ".bashrc", but this flag is for people - who do not want to pay the overhead for non-interactive - shells by having a lean .bashrc file (they set most of - the things up in .bash_profile). - -:: - A remote host that houses the repository. When this - part is specified, 'git-upload-pack' is invoked via - ssh. - -:: - The repository to sync from. - -...:: - The heads to update. This is relative to $GIT_DIR - (e.g. "HEAD", "refs/heads/master"). When unspecified, - all heads are updated to match the remote repository. -+ -Usually all the refs from existing repository are stored -under the same name in the new repository. Giving explicit - arguments instead writes the object names and refs to -the standard output, just like get-fetch-pack does. - -Author ------- -Written by Linus Torvalds - -Documentation --------------- -Documentation by Junio C Hamano. - - -GIT ---- -Part of the gitlink:git[7] suite - diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index a90521e513..f973c64313 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -62,7 +62,7 @@ OPTIONS --quiet:: -q:: Operate quietly. This flag is passed to "rsync" and - "git-clone-pack" commands when given. + "git-fetch-pack" commands when given. -n:: No checkout of HEAD is performed after the clone is complete. @@ -85,7 +85,7 @@ OPTIONS --upload-pack :: -u :: When given, and the repository to clone from is handled - by 'git-clone-pack', '--exec=' is passed to + by 'git-fetch-pack', '--exec=' is passed to the command to specify non-default path for the command run on the other end. diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt index 60debca487..f9457d45ed 100644 --- a/Documentation/git-receive-pack.txt +++ b/Documentation/git-receive-pack.txt @@ -18,8 +18,7 @@ information fed from the remote end. This command is usually not invoked directly by the end user. The UI for the protocol is on the 'git-send-pack' side, and the program pair is meant to be used to push updates to remote -repository. For pull operations, see 'git-fetch-pack' and -'git-clone-pack'. +repository. For pull operations, see 'git-fetch-pack'. The command allows for creation and fast forwarding of sha1 refs (heads/tags) on the remote end (strictly speaking, it is the diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt index 4795e98754..b2c9307661 100644 --- a/Documentation/git-upload-pack.txt +++ b/Documentation/git-upload-pack.txt @@ -12,7 +12,7 @@ SYNOPSIS DESCRIPTION ----------- -Invoked by 'git-clone-pack' and/or 'git-fetch-pack', learns what +Invoked by 'git-fetch-pack', learns what objects the other side is missing, and sends them after packing. This command is usually not invoked directly by the end user. diff --git a/Documentation/git.txt b/Documentation/git.txt index d4472b56d1..51f20c6e67 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -192,10 +192,6 @@ the working tree. Synching repositories ~~~~~~~~~~~~~~~~~~~~~ -gitlink:git-clone-pack[1]:: - Clones a repository into the current repository (engine - for ssh and local transport). - gitlink:git-fetch-pack[1]:: Updates from a remote repository (engine for ssh and local transport). @@ -237,7 +233,7 @@ gitlink:git-update-server-info[1]:: clients discover references and packs on it. gitlink:git-upload-pack[1]:: - Invoked by 'git-clone-pack' and 'git-fetch-pack' to push + Invoked by 'git-fetch-pack' to push what are asked for. gitlink:git-upload-tar[1]:: diff --git a/INSTALL b/INSTALL index 63af8eccf3..f8337e2a4d 100644 --- a/INSTALL +++ b/INSTALL @@ -96,7 +96,7 @@ Issues of note: $ mkdir manual && cd manual $ git init-db - $ git clone-pack git://git.kernel.org/pub/scm/git/git.git man html | + $ git fetch-pack git://git.kernel.org/pub/scm/git/git.git man html | while read a b do echo $a >.git/$b diff --git a/Makefile b/Makefile index 0887945ffa..ae5e8d790a 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,7 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-checkout-index$X git-clone-pack$X \ + git-checkout-index$X \ git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-mailinfo$X git-merge-base$X \ diff --git a/clone-pack.c b/clone-pack.c deleted file mode 100644 index a4370f595f..0000000000 --- a/clone-pack.c +++ /dev/null @@ -1,186 +0,0 @@ -#include "cache.h" -#include "refs.h" -#include "pkt-line.h" - -static const char clone_pack_usage[] = -"git-clone-pack [--exec=] [:] []*"; -static const char *exec = "git-upload-pack"; - -static int quiet = 0; - -static void clone_handshake(int fd[2], struct ref *ref) -{ - unsigned char sha1[20]; - - while (ref) { - packet_write(fd[1], "want %s\n", sha1_to_hex(ref->old_sha1)); - ref = ref->next; - } - packet_flush(fd[1]); - - /* We don't have nuttin' */ - packet_write(fd[1], "done\n"); - if (get_ack(fd[0], sha1)) - error("Huh! git-clone-pack got positive ack for %s", sha1_to_hex(sha1)); -} - -static int is_master(struct ref *ref) -{ - return !strcmp(ref->name, "refs/heads/master"); -} - -static void write_one_ref(struct ref *ref) -{ - char *path = git_path("%s", ref->name); - int fd; - char *hex; - - if (!strncmp(ref->name, "refs/", 5) && - check_ref_format(ref->name + 5)) { - error("refusing to create funny ref '%s' locally", ref->name); - return; - } - - if (safe_create_leading_directories(path)) - die("unable to create leading directory for %s", ref->name); - fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (fd < 0) - die("unable to create ref %s", ref->name); - hex = sha1_to_hex(ref->old_sha1); - hex[40] = '\n'; - if (write(fd, hex, 41) != 41) - die("unable to write ref %s", ref->name); - close(fd); -} - -static void write_refs(struct ref *ref) -{ - struct ref *head = NULL, *head_ptr, *master_ref; - char *head_path; - - /* Upload-pack must report HEAD first */ - if (!strcmp(ref->name, "HEAD")) { - head = ref; - ref = ref->next; - } - head_ptr = NULL; - master_ref = NULL; - while (ref) { - if (is_master(ref)) - master_ref = ref; - if (head && - !memcmp(ref->old_sha1, head->old_sha1, 20) && - !strncmp(ref->name, "refs/heads/",11) && - (!head_ptr || ref == master_ref)) - head_ptr = ref; - - write_one_ref(ref); - ref = ref->next; - } - if (!head) { - fprintf(stderr, "No HEAD in remote.\n"); - return; - } - - head_path = strdup(git_path("HEAD")); - if (!head_ptr) { - /* - * If we had a master ref, and it wasn't HEAD, we need to undo the - * symlink, and write a standalone HEAD. Give a warning, because that's - * really really wrong. - */ - if (master_ref) { - error("HEAD doesn't point to any refs! Making standalone HEAD"); - unlink(head_path); - } - write_one_ref(head); - free(head_path); - return; - } - - /* We reset to the master branch if it's available */ - if (master_ref) - return; - - fprintf(stderr, "Setting HEAD to %s\n", head_ptr->name); - - /* - * Uhhuh. Other end didn't have master. We start HEAD off with - * the first branch with the same value. - */ - if (create_symref(head_path, head_ptr->name) < 0) - die("unable to link HEAD to %s", head_ptr->name); - free(head_path); -} - -static int clone_pack(int fd[2], int nr_match, char **match) -{ - struct ref *refs; - int status; - - get_remote_heads(fd[0], &refs, nr_match, match, 1); - if (!refs) { - packet_flush(fd[1]); - die("no matching remote head"); - } - clone_handshake(fd, refs); - - status = receive_keep_pack(fd, "git-clone-pack", quiet); - if (!quiet) - fprintf(stderr, "\n"); - - if (!status) { - if (nr_match == 0) - write_refs(refs); - else - while (refs) { - printf("%s %s\n", - sha1_to_hex(refs->old_sha1), - refs->name); - refs = refs->next; - } - } - return status; -} - -int main(int argc, char **argv) -{ - int i, ret, nr_heads; - char *dest = NULL, **heads; - int fd[2]; - pid_t pid; - - setup_git_directory(); - - nr_heads = 0; - heads = NULL; - for (i = 1; i < argc; i++) { - char *arg = argv[i]; - - if (*arg == '-') { - if (!strcmp("-q", arg)) { - quiet = 1; - continue; - } - if (!strncmp("--exec=", arg, 7)) { - exec = arg + 7; - continue; - } - usage(clone_pack_usage); - } - dest = arg; - heads = argv + i + 1; - nr_heads = argc - i - 1; - break; - } - if (!dest) - usage(clone_pack_usage); - pid = git_connect(fd, dest, exec); - if (pid < 0) - return 1; - ret = clone_pack(fd, nr_heads, heads); - close(fd[0]); - close(fd[1]); - finish_connect(pid); - return ret; -} From 583b7ea31b7c16f872b178d541591ab816d16f85 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 Jun 2006 00:30:21 -0700 Subject: [PATCH 5/5] upload-pack/fetch-pack: support side-band communication This implements a protocol extension between fetch-pack and upload-pack to allow stderr stream from upload-pack (primarily used for the progress bar display) to be passed back. Signed-off-by: Junio C Hamano --- cache.h | 4 +-- fetch-clone.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++---- fetch-pack.c | 22 +++++++++++----- pkt-line.c | 4 ++- pkt-line.h | 1 + upload-pack.c | 60 ++++++++++++++++++++++++++++++++++++------- 6 files changed, 139 insertions(+), 23 deletions(-) diff --git a/cache.h b/cache.h index eaa5c0c356..efeafea70f 100644 --- a/cache.h +++ b/cache.h @@ -374,8 +374,8 @@ extern char git_commit_encoding[MAX_ENCODING_LENGTH]; extern int copy_fd(int ifd, int ofd); /* Finish off pack transfer receiving end */ -extern int receive_unpack_pack(int fd[2], const char *me, int quiet); -extern int receive_keep_pack(int fd[2], const char *me, int quiet); +extern int receive_unpack_pack(int fd[2], const char *me, int quiet, int); +extern int receive_keep_pack(int fd[2], const char *me, int quiet, int); /* pager.c */ extern void setup_pager(void); diff --git a/fetch-clone.c b/fetch-clone.c index da1b3ffbaa..c16b0c481b 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -1,5 +1,6 @@ #include "cache.h" #include "exec_cmd.h" +#include "pkt-line.h" #include #include @@ -23,7 +24,7 @@ static int finish_pack(const char *pack_tmp_name, const char *me) pid = fork(); if (pid < 0) - die("git-clone-pack: unable to fork off git-index-pack"); + die("%s: unable to fork off git-index-pack", me); if (!pid) { close(0); dup2(pipe_fd[1], 1); @@ -94,11 +95,69 @@ static int finish_pack(const char *pack_tmp_name, const char *me) exit(1); } -int receive_unpack_pack(int fd[2], const char *me, int quiet) +static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2]) +{ + pid_t side_pid; + + if (!sideband) { + fd[0] = xd[0]; + fd[1] = xd[1]; + return 0; + } + /* xd[] is talking with upload-pack; subprocess reads from + * xd[0], spits out band#2 to stderr, and feeds us band#1 + * through our fd[0]. + */ + if (pipe(fd) < 0) + die("%s: unable to set up pipe", me); + side_pid = fork(); + if (side_pid < 0) + die("%s: unable to fork off sideband demultiplexer", me); + if (!side_pid) { + /* subprocess */ + close(fd[0]); + if (xd[0] != xd[1]) + close(xd[1]); + while (1) { + char buf[1024]; + int len = packet_read_line(xd[0], buf, sizeof(buf)); + if (len == 0) + break; + if (len < 1) + die("%s: protocol error: no band designator", + me); + len--; + switch (buf[0] & 0xFF) { + case 3: + safe_write(2, buf+1, len); + fprintf(stderr, "\n"); + exit(1); + case 2: + safe_write(2, buf+1, len); + continue; + case 1: + safe_write(fd[1], buf+1, len); + continue; + default: + die("%s: protocol error: bad band #%d", + me, (buf[0] & 0xFF)); + } + } + exit(0); + } + close(xd[0]); + close(fd[1]); + fd[1] = xd[1]; + return side_pid; +} + +int receive_unpack_pack(int xd[2], const char *me, int quiet, int sideband) { int status; - pid_t pid; + pid_t pid, side_pid; + int fd[2]; + side_pid = setup_sideband(sideband, me, fd, xd); pid = fork(); if (pid < 0) die("%s: unable to fork off git-unpack-objects", me); @@ -147,10 +206,10 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet) */ #define usec_to_binarymsec(x) ((int)(x) / (1000512 >> 10)) -int receive_keep_pack(int fd[2], const char *me, int quiet) +int receive_keep_pack(int xd[2], const char *me, int quiet, int sideband) { char tmpfile[PATH_MAX]; - int ofd, ifd; + int ofd, ifd, fd[2]; unsigned long total; static struct timeval prev_tv; struct average { @@ -160,6 +219,8 @@ int receive_keep_pack(int fd[2], const char *me, int quiet) unsigned long avg_bytes, avg_time; int idx = 0; + setup_sideband(sideband, me, fd, xd); + ifd = fd[0]; snprintf(tmpfile, sizeof(tmpfile), "%s/pack/tmp-XXXXXX", get_object_directory()); diff --git a/fetch-pack.c b/fetch-pack.c index 7d23a8071a..f2c51ebe4b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -25,7 +25,7 @@ static const char *exec = "git-upload-pack"; #define MAX_IN_VAIN 256 static struct commit_list *rev_list = NULL; -static int non_common_revs = 0, multi_ack = 0, use_thin_pack = 0; +static int non_common_revs = 0, multi_ack = 0, use_thin_pack = 0, use_sideband; static void rev_list_push(struct commit *commit, int mark) { @@ -165,9 +165,14 @@ static int find_common(int fd[2], unsigned char *result_sha1, continue; } - packet_write(fd[1], "want %s%s%s\n", sha1_to_hex(remote), - (multi_ack ? " multi_ack" : ""), - (use_thin_pack ? " thin-pack" : "")); + if (!fetching) + packet_write(fd[1], "want %s%s%s%s\n", + sha1_to_hex(remote), + (multi_ack ? " multi_ack" : ""), + (use_sideband ? " side-band" : ""), + (use_thin_pack ? " thin-pack" : "")); + else + packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); fetching++; } packet_flush(fd[1]); @@ -421,6 +426,11 @@ static int fetch_pack(int fd[2], int nr_match, char **match) fprintf(stderr, "Server supports multi_ack\n"); multi_ack = 1; } + if (server_supports("side-band")) { + if (verbose) + fprintf(stderr, "Server supports side-band\n"); + use_sideband = 1; + } if (!ref) { packet_flush(fd[1]); die("no matching remote head"); @@ -437,9 +447,9 @@ static int fetch_pack(int fd[2], int nr_match, char **match) fprintf(stderr, "warning: no common commits\n"); if (keep_pack) - status = receive_keep_pack(fd, "git-fetch-pack", quiet); + status = receive_keep_pack(fd, "git-fetch-pack", quiet, use_sideband); else - status = receive_unpack_pack(fd, "git-fetch-pack", quiet); + status = receive_unpack_pack(fd, "git-fetch-pack", quiet, use_sideband); if (status) die("git-fetch-pack: fetch failed."); diff --git a/pkt-line.c b/pkt-line.c index bb3bab05cd..3d724acf23 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -16,8 +16,9 @@ * The writing side could use stdio, but since the reading * side can't, we stay with pure read/write interfaces. */ -static void safe_write(int fd, const void *buf, unsigned n) +ssize_t safe_write(int fd, const void *buf, ssize_t n) { + ssize_t nn = n; while (n) { int ret = xwrite(fd, buf, n); if (ret > 0) { @@ -29,6 +30,7 @@ static void safe_write(int fd, const void *buf, unsigned n) die("write error (disk full?)"); die("write error (%s)", strerror(errno)); } + return nn; } /* diff --git a/pkt-line.h b/pkt-line.h index 51d0cbe219..9abef24de3 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -8,5 +8,6 @@ void packet_flush(int fd); void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3))); int packet_read_line(int fd, char *buffer, unsigned size); +ssize_t safe_write(int, const void *, ssize_t); #endif diff --git a/upload-pack.c b/upload-pack.c index 13eaa22780..7b86f6965b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -21,6 +21,7 @@ static int use_thin_pack = 0; static unsigned char has_sha1[MAX_HAS][20]; static unsigned char needs_sha1[MAX_NEEDS][20]; static unsigned int timeout = 0; +static int use_sideband = 0; static void reset_timeout(void) { @@ -34,6 +35,43 @@ static int strip(char *line, int len) return len; } +#define PACKET_MAX 1000 +static ssize_t send_client_data(int fd, const char *data, ssize_t sz) +{ + ssize_t ssz; + const char *p; + + if (!data) { + if (!use_sideband) + return 0; + packet_flush(1); + } + + if (!use_sideband) { + if (fd == 3) + /* emergency quit */ + fd = 2; + return safe_write(fd, data, sz); + } + p = data; + ssz = sz; + while (sz) { + unsigned n; + char hdr[5]; + + n = sz; + if (PACKET_MAX - 5 < n) + n = PACKET_MAX - 5; + sprintf(hdr, "%04x", n + 5); + hdr[4] = fd; + safe_write(1, hdr, 5); + safe_write(1, p, n); + p += n; + sz -= n; + } + return ssz; +} + static void create_pack_file(void) { /* Pipes between rev-list to pack-objects, pack-objects to us @@ -43,6 +81,8 @@ static void create_pack_file(void) pid_t pid_rev_list, pid_pack_objects; int create_full_pack = (nr_our_refs == nr_needs && !nr_has); char data[8193], progress[128]; + char abort_msg[] = "aborting due to possible repository " + "corruption on the remote side."; int buffered = -1; if (pipe(lp_pipe) < 0) @@ -132,7 +172,6 @@ static void create_pack_file(void) while (1) { const char *who; - char *cp; struct pollfd pfd[2]; pid_t pid; int status; @@ -199,19 +238,18 @@ static void create_pack_file(void) } else buffered = -1; - sz = xwrite(1, data, sz); + sz = send_client_data(1, data, sz); if (sz < 0) goto fail; } if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { - /* Status ready; we do not use it for now, - * but later we will add side-band to send it - * to the other side. + /* Status ready; we ship that in the side-band + * or dump to the standard error. */ sz = read(pe_pipe[0], progress, sizeof(progress)); if (0 < sz) - write(2, progress, sz); + send_client_data(2, progress, sz); else if (sz == 0) { close(pe_pipe[0]); pe_pipe[0] = -1; @@ -259,11 +297,12 @@ static void create_pack_file(void) /* flush the data */ if (0 <= buffered) { data[0] = buffered; - sz = xwrite(1, data, 1); + sz = send_client_data(1, data, 1); if (sz < 0) goto fail; fprintf(stderr, "flushed.\n"); } + send_client_data(1, NULL, 0); return; } fail: @@ -271,7 +310,8 @@ static void create_pack_file(void) kill(pid_pack_objects, SIGKILL); if (pid_rev_list) kill(pid_rev_list, SIGKILL); - die("git-upload-pack: aborting due to possible repository corruption on the remote side."); + send_client_data(3, abort_msg, sizeof(abort_msg)); + die("git-upload-pack: %s", abort_msg); } static int got_sha1(char *hex, unsigned char *sha1) @@ -378,6 +418,8 @@ static int receive_needs(void) multi_ack = 1; if (strstr(line+45, "thin-pack")) use_thin_pack = 1; + if (strstr(line+45, "side-band")) + use_sideband = 1; /* We have sent all our refs already, and the other end * should have chosen out of them; otherwise they are @@ -399,7 +441,7 @@ static int receive_needs(void) static int send_ref(const char *refname, const unsigned char *sha1) { - static char *capabilities = "multi_ack thin-pack"; + static char *capabilities = "multi_ack thin-pack side-band"; struct object *o = parse_object(sha1); if (!o)