#include "cache.h"
#include "commit.h"
#include "pack.h"
#include "fetch.h"
#include "http.h"
#define PREV_BUF_SIZE 4096
#define RANGE_HEADER_SIZE 30
static int commits_on_stdin;
static int got_alternates = -1;
static int corrupt_object_found;
static struct curl_slist *no_pragma_header;
struct alt_base
{
const char *base;
int path_len;
int got_indices;
struct packed_git *packs;
struct alt_base *next;
};
static struct alt_base *alt;
enum object_request_state {
WAITING,
ABORTED,
ACTIVE,
COMPLETE,
};
struct object_request
{
unsigned char sha1[20];
struct alt_base *repo;
char *url;
char filename[PATH_MAX];
char tmpfile[PATH_MAX];
int local;
enum object_request_state state;
CURLcode curl_result;
char errorstr[CURL_ERROR_SIZE];
long http_code;
unsigned char real_sha1[20];
SHA_CTX c;
z_stream stream;
int zret;
int rename;
struct active_request_slot *slot;
struct object_request *next;
};
struct alternates_request {
const char *base;
char *url;
struct buffer *buffer;
struct active_request_slot *slot;
int http_specific;
};
static struct object_request *object_queue_head;
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
void *data)
{
unsigned char expn[4096];
size_t size = eltsize * nmemb;
int posn = 0;
struct object_request *obj_req = (struct object_request *)data;
do {
ssize_t retval = xwrite(obj_req->local,
(char *) ptr + posn, size - posn);
if (retval < 0)
return posn;
posn += retval;
} while (posn < size);
obj_req->stream.avail_in = size;
obj_req->stream.next_in = ptr;
do {
obj_req->stream.next_out = expn;
obj_req->stream.avail_out = sizeof(expn);
obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
SHA1_Update(&obj_req->c, expn,
sizeof(expn) - obj_req->stream.avail_out);
} while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
data_received++;
return size;
}
static int missing__target(int code, int result)
{
return /* file:// URL -- do we ever use one??? */
(result == CURLE_FILE_COULDNT_READ_FILE) ||
/* http:// and https:// URL */
(code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
/* ftp:// URL */
(code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
;
}
#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
static void fetch_alternates(const char *base);
static void process_object_response(void *callback_data);
static void start_object_request(struct object_request *obj_req)
{
char *hex = sha1_to_hex(obj_req->sha1);
char prevfile[PATH_MAX];
char *url;
char *posn;
int prevlocal;
unsigned char prev_buf[PREV_BUF_SIZE];
ssize_t prev_read = 0;
long prev_posn = 0;
char range[RANGE_HEADER_SIZE];
struct curl_slist *range_header = NULL;
struct active_request_slot *slot;
snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
unlink(prevfile);
rename(obj_req->tmpfile, prevfile);
unlink(obj_req->tmpfile);
if (obj_req->local != -1)
error("fd leakage in start: %d", obj_req->local);
obj_req->local = open(obj_req->tmpfile,
O_WRONLY | O_CREAT | O_EXCL, 0666);
/* This could have failed due to the "lazy directory creation";
* try to mkdir the last path component.
*/
if (obj_req->local < 0 && errno == ENOENT) {
char *dir = strrchr(obj_req->tmpfile, '/');
if (dir) {
*dir = 0;
mkdir(obj_req->tmpfile, 0777);
*dir = '/';
}
obj_req->local = open(obj_req->tmpfile,
O_WRONLY | O_CREAT | O_EXCL, 0666);
}
if (obj_req->local < 0) {
obj_req->state = ABORTED;
error("Couldn't create temporary file %s for %s: %s",
obj_req->tmpfile, obj_req->filename, strerror(errno));
return;
}
memset(&obj_req->stream, 0, sizeof(obj_req->stream));
inflateInit(&obj_req->stream);
SHA1_Init(&obj_req->c);
url = xmalloc(strlen(obj_req->repo->base) + 50);
obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50);
strcpy(url, obj_req->repo->base);
posn = url + strlen(obj_req->repo->base);
strcpy(posn, "objects/");
posn += 8;
memcpy(posn, hex, 2);
posn += 2;
*(posn++) = '/';
strcpy(posn, hex + 2);
strcpy(obj_req->url, url);
/* If a previous temp file is present, process what was already
fetched. */
prevlocal = open(prevfile, O_RDONLY);
if (prevlocal != -1) {
do {
prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
if (prev_read>0) {
if (fwrite_sha1_file(prev_buf,
1,
prev_read,
obj_req) == prev_read) {
prev_posn += prev_read;
} else {
prev_read = -1;
}
}
} while (prev_read > 0);
close(prevlocal);
}
unlink(prevfile);
/* Reset inflate/SHA1 if there was an error reading the previous temp
file; also rewind to the beginning of the local file. */
if (prev_read == -1) {
memset(&obj_req->stream, 0, sizeof(obj_req->stream));
inflateInit(&obj_req->stream);
SHA1_Init(&obj_req->c);
if (prev_posn>0) {
prev_posn = 0;
lseek(obj_req->local, SEEK_SET, 0);
|