Skip to content

Commit

Permalink
bundle: allowing to read from an unseekable fd
Browse files Browse the repository at this point in the history
We wished that "git bundle" to eventually learn to read from a network
socket which is not seekable. The current code opens with fopen(), reads
the file halfway and run ftell(), and reopens the same file with open()
and seeks, to skip the header.

This patch by itself does not reach that goal yet, but I think it is a
right step in that direction.

Signed-off-by: Junio C Hamano <[email protected]>
  • Loading branch information
gitster committed Oct 13, 2011
1 parent f696543 commit e9ee84c
Showing 1 changed file with 64 additions and 35 deletions.
99 changes: 64 additions & 35 deletions bundle.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,49 +23,78 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name,
list->nr++;
}

/* returns an fd */
/* Eventually this should go to strbuf.[ch] */
static int strbuf_readline_fd(struct strbuf *sb, int fd)
{
strbuf_reset(sb);

while (1) {
char ch;
ssize_t len = xread(fd, &ch, 1);
if (len < 0)
return -1;
strbuf_addch(sb, ch);
if (ch == '\n')
break;
}
return 0;
}

int read_bundle_header(const char *path, struct bundle_header *header)
{
char buffer[1024];
int fd;
long fpos;
FILE *ffd = fopen(path, "rb");
struct strbuf buf = STRBUF_INIT;
int fd = open(path, O_RDONLY);
int status = 0;

if (!ffd)
if (fd < 0)
return error("could not open '%s'", path);
if (!fgets(buffer, sizeof(buffer), ffd) ||
strcmp(buffer, bundle_signature)) {
fclose(ffd);
return error("'%s' does not look like a v2 bundle file", path);

/* The bundle header begins with the signature */
if (strbuf_readline_fd(&buf, fd) ||
strcmp(buf.buf, bundle_signature)) {
error("'%s' does not look like a v2 bundle file", path);
status = -1;
goto abort;
}
while (fgets(buffer, sizeof(buffer), ffd)
&& buffer[0] != '\n') {
int is_prereq = buffer[0] == '-';
int offset = is_prereq ? 1 : 0;
int len = strlen(buffer);

/* The bundle header ends with an empty line */
while (!strbuf_readline_fd(&buf, fd) &&
buf.len && buf.buf[0] != '\n') {
unsigned char sha1[20];
struct ref_list *list = is_prereq ? &header->prerequisites
: &header->references;
char delim;

if (len && buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if (get_sha1_hex(buffer + offset, sha1)) {
warning("unrecognized header: %s", buffer);
continue;
int is_prereq = 0;

if (*buf.buf == '-') {
is_prereq = 1;
strbuf_remove(&buf, 0, 1);
}
strbuf_rtrim(&buf);

/*
* Tip lines have object name, SP, and refname.
* Prerequisites have object name that is optionally
* followed by SP and subject line.
*/
if (get_sha1_hex(buf.buf, sha1) ||
(40 <= buf.len && !isspace(buf.buf[40])) ||
(!is_prereq && buf.len <= 40)) {
error("unrecognized header: %s%s (%d)",
(is_prereq ? "-" : ""), buf.buf, (int)buf.len);
status = -1;
break;
} else {
if (is_prereq)
add_to_ref_list(sha1, "", &header->prerequisites);
else
add_to_ref_list(sha1, buf.buf + 41, &header->references);
}
delim = buffer[40 + offset];
if (!isspace(delim) && (delim != '\0' || !is_prereq))
die ("invalid header: %s", buffer);
add_to_ref_list(sha1, isspace(delim) ?
buffer + 41 + offset : "", list);
}
fpos = ftell(ffd);
fclose(ffd);
fd = open(path, O_RDONLY);
if (fd < 0)
return error("could not open '%s'", path);
lseek(fd, fpos, SEEK_SET);

abort:
if (status) {
close(fd);
fd = -1;
}
strbuf_release(&buf);
return fd;
}

Expand Down

0 comments on commit e9ee84c

Please sign in to comment.