Skip to content

Commit b76c561

Browse files
committed
Merge branch 'jc/unseekable-bundle'
* jc/unseekable-bundle: bundle: add parse_bundle_header() helper function bundle: allowing to read from an unseekable fd Conflicts: transport.c
2 parents afd6284 + 2727b71 commit b76c561

File tree

3 files changed

+90
-59
lines changed

3 files changed

+90
-59
lines changed

bundle.c

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,50 +23,102 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name,
2323
list->nr++;
2424
}
2525

26-
/* returns an fd */
27-
int read_bundle_header(const char *path, struct bundle_header *header)
26+
/* Eventually this should go to strbuf.[ch] */
27+
static int strbuf_readline_fd(struct strbuf *sb, int fd)
2828
{
29-
char buffer[1024];
30-
int fd;
31-
long fpos;
32-
FILE *ffd = fopen(path, "rb");
29+
strbuf_reset(sb);
30+
31+
while (1) {
32+
char ch;
33+
ssize_t len = xread(fd, &ch, 1);
34+
if (len < 0)
35+
return -1;
36+
strbuf_addch(sb, ch);
37+
if (ch == '\n')
38+
break;
39+
}
40+
return 0;
41+
}
3342

34-
if (!ffd)
35-
return error("could not open '%s'", path);
36-
if (!fgets(buffer, sizeof(buffer), ffd) ||
37-
strcmp(buffer, bundle_signature)) {
38-
fclose(ffd);
39-
return error("'%s' does not look like a v2 bundle file", path);
43+
static int parse_bundle_header(int fd, struct bundle_header *header,
44+
const char *report_path)
45+
{
46+
struct strbuf buf = STRBUF_INIT;
47+
int status = 0;
48+
49+
/* The bundle header begins with the signature */
50+
if (strbuf_readline_fd(&buf, fd) ||
51+
strcmp(buf.buf, bundle_signature)) {
52+
if (report_path)
53+
error("'%s' does not look like a v2 bundle file",
54+
report_path);
55+
status = -1;
56+
goto abort;
4057
}
41-
while (fgets(buffer, sizeof(buffer), ffd)
42-
&& buffer[0] != '\n') {
43-
int is_prereq = buffer[0] == '-';
44-
int offset = is_prereq ? 1 : 0;
45-
int len = strlen(buffer);
58+
59+
/* The bundle header ends with an empty line */
60+
while (!strbuf_readline_fd(&buf, fd) &&
61+
buf.len && buf.buf[0] != '\n') {
4662
unsigned char sha1[20];
47-
struct ref_list *list = is_prereq ? &header->prerequisites
48-
: &header->references;
49-
char delim;
50-
51-
if (len && buffer[len - 1] == '\n')
52-
buffer[len - 1] = '\0';
53-
if (get_sha1_hex(buffer + offset, sha1)) {
54-
warning("unrecognized header: %s", buffer);
55-
continue;
63+
int is_prereq = 0;
64+
65+
if (*buf.buf == '-') {
66+
is_prereq = 1;
67+
strbuf_remove(&buf, 0, 1);
68+
}
69+
strbuf_rtrim(&buf);
70+
71+
/*
72+
* Tip lines have object name, SP, and refname.
73+
* Prerequisites have object name that is optionally
74+
* followed by SP and subject line.
75+
*/
76+
if (get_sha1_hex(buf.buf, sha1) ||
77+
(40 <= buf.len && !isspace(buf.buf[40])) ||
78+
(!is_prereq && buf.len <= 40)) {
79+
if (report_path)
80+
error("unrecognized header: %s%s (%d)",
81+
(is_prereq ? "-" : ""), buf.buf, (int)buf.len);
82+
status = -1;
83+
break;
84+
} else {
85+
if (is_prereq)
86+
add_to_ref_list(sha1, "", &header->prerequisites);
87+
else
88+
add_to_ref_list(sha1, buf.buf + 41, &header->references);
5689
}
57-
delim = buffer[40 + offset];
58-
if (!isspace(delim) && (delim != '\0' || !is_prereq))
59-
die ("invalid header: %s", buffer);
60-
add_to_ref_list(sha1, isspace(delim) ?
61-
buffer + 41 + offset : "", list);
6290
}
63-
fpos = ftell(ffd);
64-
fclose(ffd);
65-
fd = open(path, O_RDONLY);
91+
92+
abort:
93+
if (status) {
94+
close(fd);
95+
fd = -1;
96+
}
97+
strbuf_release(&buf);
98+
return fd;
99+
}
100+
101+
int read_bundle_header(const char *path, struct bundle_header *header)
102+
{
103+
int fd = open(path, O_RDONLY);
104+
66105
if (fd < 0)
67106
return error("could not open '%s'", path);
68-
lseek(fd, fpos, SEEK_SET);
69-
return fd;
107+
return parse_bundle_header(fd, header, path);
108+
}
109+
110+
int is_bundle(const char *path, int quiet)
111+
{
112+
struct bundle_header header;
113+
int fd = open(path, O_RDONLY);
114+
115+
if (fd < 0)
116+
return 0;
117+
memset(&header, 0, sizeof(header));
118+
fd = parse_bundle_header(fd, &header, quiet ? NULL : path);
119+
if (fd >= 0)
120+
close(fd);
121+
return (fd >= 0);
70122
}
71123

72124
static int list_refs(struct ref_list *r, int argc, const char **argv)

bundle.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct bundle_header {
1414
struct ref_list references;
1515
};
1616

17+
int is_bundle(const char *path, int quiet);
1718
int read_bundle_header(const char *path, struct bundle_header *header);
1819
int create_bundle(struct bundle_header *header, const char *path,
1920
int argc, const char **argv);

transport.c

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -859,28 +859,6 @@ static int is_local(const char *url)
859859
has_dos_drive_prefix(url);
860860
}
861861

862-
static int is_gitfile(const char *url)
863-
{
864-
struct stat st;
865-
char buf[9];
866-
int fd, len;
867-
if (stat(url, &st))
868-
return 0;
869-
if (!S_ISREG(st.st_mode))
870-
return 0;
871-
if (st.st_size < 10 || st.st_size > 9 + PATH_MAX)
872-
return 0;
873-
874-
fd = open(url, O_RDONLY);
875-
if (fd < 0)
876-
die_errno("Error opening '%s'", url);
877-
len = read_in_full(fd, buf, sizeof(buf));
878-
close(fd);
879-
if (len != sizeof(buf))
880-
die("Error reading %s", url);
881-
return !prefixcmp(buf, "gitdir: ");
882-
}
883-
884862
static int is_file(const char *url)
885863
{
886864
struct stat buf;
@@ -929,7 +907,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
929907
ret->fetch = fetch_objs_via_rsync;
930908
ret->push = rsync_transport_push;
931909
ret->smart_options = NULL;
932-
} else if (is_local(url) && is_file(url) && !is_gitfile(url)) {
910+
} else if (is_local(url) && is_file(url) && is_bundle(url, 1)) {
933911
struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
934912
ret->data = data;
935913
ret->get_refs_list = get_refs_from_bundle;

0 commit comments

Comments
 (0)