#include "commit.h"
#include "tag.h"
#include "tree.h"
+#include "refs.h"
#ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
static unsigned int sha1_file_open_flag = O_NOATIME;
-signed char hexval_table[256] = {
+const signed char hexval_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */
-1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */
-1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */
static void read_info_alternates(const char * relative_base, int depth)
{
char *map;
+ size_t mapsz;
struct stat st;
char path[PATH_MAX];
int fd;
close(fd);
return;
}
- map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ mapsz = xsize_t(st.st_size);
+ map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth);
+ link_alt_odb_entries(map, map + mapsz, '\n', relative_base, depth);
- munmap(map, st.st_size);
+ munmap(map, mapsz);
}
void prepare_alt_odb(void)
pack_mapped, peak_pack_mapped);
}
-static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
- void **idx_map_)
+static int check_packed_git_idx(const char *path, struct packed_git *p)
{
void *idx_map;
- uint32_t *index;
- unsigned long idx_size;
- int nr, i;
+ struct pack_idx_header *hdr;
+ size_t idx_size;
+ uint32_t version, nr, i, *index;
int fd = open(path, O_RDONLY);
struct stat st;
+
if (fd < 0)
return -1;
if (fstat(fd, &st)) {
close(fd);
return -1;
}
- idx_size = st.st_size;
+ idx_size = xsize_t(st.st_size);
+ if (idx_size < 4 * 256 + 20 + 20) {
+ close(fd);
+ return error("index file %s is too small", path);
+ }
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- index = idx_map;
- *idx_map_ = idx_map;
- *idx_size_ = idx_size;
-
- /* check index map */
- if (idx_size < 4*256 + 20 + 20)
- return error("index file %s is too small", path);
-
- /* a future index format would start with this, as older git
- * binaries would fail the non-monotonic index check below.
- * give a nicer warning to the user if we can.
- */
- if (index[0] == htonl(PACK_IDX_SIGNATURE))
- return error("index file %s is a newer version"
- " and is not supported by this binary"
- " (try upgrading GIT to a newer version)",
- path);
+ hdr = idx_map;
+ if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
+ version = ntohl(hdr->idx_version);
+ if (version < 2 || version > 2) {
+ munmap(idx_map, idx_size);
+ return error("index file %s is version %d"
+ " and is not supported by this binary"
+ " (try upgrading GIT to a newer version)",
+ path, version);
+ }
+ } else
+ version = 1;
nr = 0;
+ index = idx_map;
+ if (version > 1)
+ index += 2; /* skip index header */
for (i = 0; i < 256; i++) {
- unsigned int n = ntohl(index[i]);
- if (n < nr)
+ uint32_t n = ntohl(index[i]);
+ if (n < nr) {
+ munmap(idx_map, idx_size);
return error("non-monotonic index %s", path);
+ }
nr = n;
}
- /*
- * Total size:
- * - 256 index entries 4 bytes each
- * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
- */
- if (idx_size != 4*256 + nr * 24 + 20 + 20)
- return error("wrong index file size in %s", path);
+ if (version == 1) {
+ /*
+ * Total size:
+ * - 256 index entries 4 bytes each
+ * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ */
+ if (idx_size != 4*256 + nr * 24 + 20 + 20) {
+ munmap(idx_map, idx_size);
+ return error("wrong index file size in %s", path);
+ }
+ } else if (version == 2) {
+ /*
+ * Minimum size:
+ * - 8 bytes of header
+ * - 256 index entries 4 bytes each
+ * - 20-byte sha1 entry * nr
+ * - 4-byte crc entry * nr
+ * - 4-byte offset entry * nr
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ * And after the 4-byte offset table might be a
+ * variable sized table containing 8-byte entries
+ * for offsets larger than 2^31.
+ */
+ unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
+ if (idx_size < min_size || idx_size > min_size + (nr - 1)*8) {
+ munmap(idx_map, idx_size);
+ return error("wrong index file size in %s", path);
+ }
+ if (idx_size != min_size) {
+ /* make sure we can deal with large pack offsets */
+ off_t x = 0x7fffffffUL, y = 0xffffffffUL;
+ if (x > (x + 1) || y > (y + 1)) {
+ munmap(idx_map, idx_size);
+ return error("pack too large for current definition of off_t in %s", path);
+ }
+ }
+ }
+ p->index_version = version;
+ p->index_data = idx_map;
+ p->index_size = idx_size;
+ p->num_objects = nr;
return 0;
}
}
}
-static int unuse_one_window(struct packed_git *current)
+static int unuse_one_window(struct packed_git *current, int keep_fd)
{
struct packed_git *p, *lru_p = NULL;
struct pack_window *lru_w = NULL, *lru_l = NULL;
lru_l->next = lru_w->next;
else {
lru_p->windows = lru_w->next;
- if (!lru_p->windows && lru_p != current) {
+ if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
close(lru_p->pack_fd);
lru_p->pack_fd = -1;
}
return 0;
}
-void release_pack_memory(size_t need)
+void release_pack_memory(size_t need, int fd)
{
size_t cur = pack_mapped;
- while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
+ while (need >= (cur - pack_mapped) && unuse_one_window(NULL, fd))
; /* nothing */
}
p->pack_name, ntohl(hdr.hdr_version));
/* Verify the pack matches its index. */
- if (num_packed_objects(p) != ntohl(hdr.hdr_entries))
+ if (p->num_objects != ntohl(hdr.hdr_entries))
return error("packfile %s claims to have %u objects"
- " while index size indicates %u objects",
- p->pack_name, ntohl(hdr.hdr_entries),
- num_packed_objects(p));
+ " while index indicates %u objects",
+ p->pack_name, ntohl(hdr.hdr_entries),
+ p->num_objects);
if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
return error("end of packfile %s is unavailable", p->pack_name);
if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
return error("packfile %s signature is unavailable", p->pack_name);
- idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
+ idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
if (hashcmp(sha1, idx_sha1))
return error("packfile %s does not match index", p->pack_name);
return 0;
return -1;
}
-static int in_window(struct pack_window *win, unsigned long offset)
+static int in_window(struct pack_window *win, off_t offset)
{
/* We must promise at least 20 bytes (one hash) after the
* offset is available from this window, otherwise the offset
unsigned char* use_pack(struct packed_git *p,
struct pack_window **w_cursor,
- unsigned long offset,
+ off_t offset,
unsigned int *left)
{
struct pack_window *win = *w_cursor;
}
if (!win) {
size_t window_align = packed_git_window_size / 2;
+ off_t len;
win = xcalloc(1, sizeof(*win));
win->offset = (offset / window_align) * window_align;
- win->len = p->pack_size - win->offset;
- if (win->len > packed_git_window_size)
- win->len = packed_git_window_size;
+ len = p->pack_size - win->offset;
+ if (len > packed_git_window_size)
+ len = packed_git_window_size;
+ win->len = (size_t)len;
pack_mapped += win->len;
while (packed_git_limit < pack_mapped
- && unuse_one_window(p))
+ && unuse_one_window(p, p->pack_fd))
; /* nothing */
win->base = xmmap(NULL, win->len,
PROT_READ, MAP_PRIVATE,
}
offset -= win->offset;
if (left)
- *left = win->len - offset;
+ *left = win->len - xsize_t(offset);
return win->base + offset;
}
-struct packed_git *add_packed_git(char *path, int path_len, int local)
+struct packed_git *add_packed_git(const char *path, int path_len, int local)
{
struct stat st;
- struct packed_git *p;
- unsigned long idx_size;
- void *idx_map;
- unsigned char sha1[20];
+ struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2);
- if (check_packed_git_idx(path, &idx_size, &idx_map))
+ /*
+ * Make sure a corresponding .pack file exists and that
+ * the index looks sane.
+ */
+ path_len -= strlen(".idx");
+ if (path_len < 1)
return NULL;
-
- /* do we have a corresponding .pack file? */
- strcpy(path + path_len - 4, ".pack");
- if (stat(path, &st) || !S_ISREG(st.st_mode)) {
- munmap(idx_map, idx_size);
+ memcpy(p->pack_name, path, path_len);
+ strcpy(p->pack_name + path_len, ".pack");
+ if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode) ||
+ check_packed_git_idx(path, p)) {
+ free(p);
return NULL;
}
+
/* ok, it looks sane as far as we can check without
* actually mapping the pack file.
*/
- p = xmalloc(sizeof(*p) + path_len + 2);
- strcpy(p->pack_name, path);
- p->index_size = idx_size;
p->pack_size = st.st_size;
- p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
p->pack_fd = -1;
p->pack_local = local;
- if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
- hashcpy(p->sha1, sha1);
+ p->mtime = st.st_mtime;
+ if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
+ hashclr(p->sha1);
return p;
}
return parse_pack_index_file(sha1, path);
}
-struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path)
+struct packed_git *parse_pack_index_file(const unsigned char *sha1,
+ const char *idx_path)
{
- struct packed_git *p;
- unsigned long idx_size;
- void *idx_map;
- char *path;
+ const char *path = sha1_pack_name(sha1);
+ struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
- if (check_packed_git_idx(idx_path, &idx_size, &idx_map))
+ if (check_packed_git_idx(idx_path, p)) {
+ free(p);
return NULL;
+ }
- path = sha1_pack_name(sha1);
-
- p = xmalloc(sizeof(*p) + strlen(path) + 2);
strcpy(p->pack_name, path);
- p->index_size = idx_size;
p->pack_size = 0;
- p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
p->pack_fd = -1;
closedir(dir);
}
+static int sort_pack(const void *a_, const void *b_)
+{
+ struct packed_git *a = *((struct packed_git **)a_);
+ struct packed_git *b = *((struct packed_git **)b_);
+ int st;
+
+ /*
+ * Local packs tend to contain objects specific to our
+ * variant of the project than remote ones. In addition,
+ * remote ones could be on a network mounted filesystem.
+ * Favor local ones for these reasons.
+ */
+ st = a->pack_local - b->pack_local;
+ if (st)
+ return -st;
+
+ /*
+ * Younger packs tend to contain more recent objects,
+ * and more recent objects tend to get accessed more
+ * often.
+ */
+ if (a->mtime < b->mtime)
+ return 1;
+ else if (a->mtime == b->mtime)
+ return 0;
+ return -1;
+}
+
+static void rearrange_packed_git(void)
+{
+ struct packed_git **ary, *p;
+ int i, n;
+
+ for (n = 0, p = packed_git; p; p = p->next)
+ n++;
+ if (n < 2)
+ return;
+
+ /* prepare an array of packed_git for easier sorting */
+ ary = xcalloc(n, sizeof(struct packed_git *));
+ for (n = 0, p = packed_git; p; p = p->next)
+ ary[n++] = p;
+
+ qsort(ary, n, sizeof(struct packed_git *), sort_pack);
+
+ /* link them back again */
+ for (i = 0; i < n - 1; i++)
+ ary[i]->next = ary[i + 1];
+ ary[n - 1]->next = NULL;
+ packed_git = ary[0];
+
+ free(ary);
+}
+
static int prepare_packed_git_run_once = 0;
void prepare_packed_git(void)
{
prepare_packed_git_one(alt->base, 0);
alt->name[-1] = '/';
}
+ rearrange_packed_git();
prepare_packed_git_run_once = 1;
}
*/
sha1_file_open_flag = 0;
}
- map = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ *size = xsize_t(st.st_size);
+ map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- *size = st.st_size;
return map;
}
return 0;
}
-static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
+static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
{
int bytes = strlen(buffer) + 1;
unsigned char *buf = xmalloc(1+size);
unsigned long n;
+ int status = Z_OK;
n = stream->total_out - bytes;
if (n > size)
n = size;
memcpy(buf, (char *) buffer + bytes, n);
bytes = n;
- if (bytes < size) {
+ if (bytes <= size) {
+ /*
+ * The above condition must be (bytes <= size), not
+ * (bytes < size). In other words, even though we
+ * expect no more output and set avail_out to zer0,
+ * the input zlib stream may have bytes that express
+ * "this concludes the stream", and we *do* want to
+ * eat that input.
+ *
+ * Otherwise we would not be able to test that we
+ * consumed all the input to reach the expected size;
+ * we also want to check that zlib tells us that all
+ * went well with status == Z_STREAM_END at the end.
+ */
stream->next_out = buf + bytes;
stream->avail_out = size - bytes;
- while (inflate(stream, Z_FINISH) == Z_OK)
- /* nothing */;
+ while (status == Z_OK)
+ status = inflate(stream, Z_FINISH);
}
buf[size] = 0;
- inflateEnd(stream);
- return buf;
+ if (status == Z_STREAM_END && !stream->avail_in) {
+ inflateEnd(stream);
+ return buf;
+ }
+
+ if (status < 0)
+ error("corrupt loose object '%s'", sha1_to_hex(sha1));
+ else if (stream->avail_in)
+ error("garbage at end of loose object '%s'",
+ sha1_to_hex(sha1));
+ free(buf);
+ return NULL;
}
/*
return *hdr ? -1 : type_from_string(type);
}
-void * unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size)
+static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
{
int ret;
z_stream stream;
if (ret < Z_OK || (*type = parse_sha1_header(hdr, size)) < 0)
return NULL;
- return unpack_sha1_rest(&stream, hdr, *size);
+ return unpack_sha1_rest(&stream, hdr, *size, sha1);
}
-static unsigned long get_delta_base(struct packed_git *p,
+unsigned long get_size_from_delta(struct packed_git *p,
+ struct pack_window **w_curs,
+ off_t curpos)
+{
+ const unsigned char *data;
+ unsigned char delta_head[20], *in;
+ z_stream stream;
+ int st;
+
+ memset(&stream, 0, sizeof(stream));
+ stream.next_out = delta_head;
+ stream.avail_out = sizeof(delta_head);
+
+ inflateInit(&stream);
+ do {
+ in = use_pack(p, w_curs, curpos, &stream.avail_in);
+ stream.next_in = in;
+ st = inflate(&stream, Z_FINISH);
+ curpos += stream.next_in - in;
+ } while ((st == Z_OK || st == Z_BUF_ERROR) &&
+ stream.total_out < sizeof(delta_head));
+ inflateEnd(&stream);
+ if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
+ die("delta data unpack-initial failed");
+
+ /* Examine the initial part of the delta to figure out
+ * the result size.
+ */
+ data = delta_head;
+
+ /* ignore base size */
+ get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+
+ /* Read the result size */
+ return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+}
+
+static off_t get_delta_base(struct packed_git *p,
struct pack_window **w_curs,
- unsigned long *curpos,
+ off_t *curpos,
enum object_type type,
- unsigned long delta_obj_offset)
+ off_t delta_obj_offset)
{
unsigned char *base_info = use_pack(p, w_curs, *curpos, NULL);
- unsigned long base_offset;
+ off_t base_offset;
/* use_pack() assured us we have [base_info, base_info + 20)
* as a range that we can look at without walking off the
base_offset = c & 127;
while (c & 128) {
base_offset += 1;
- if (!base_offset || base_offset & ~(~0UL >> 7))
+ if (!base_offset || MSB(base_offset, 7))
die("offset value overflow for delta base object");
c = base_info[used++];
base_offset = (base_offset << 7) + (c & 127);
}
/* forward declaration for a mutually recursive function */
-static int packed_object_info(struct packed_git *p, unsigned long offset,
+static int packed_object_info(struct packed_git *p, off_t offset,
unsigned long *sizep);
static int packed_delta_info(struct packed_git *p,
struct pack_window **w_curs,
- unsigned long curpos,
+ off_t curpos,
enum object_type type,
- unsigned long obj_offset,
+ off_t obj_offset,
unsigned long *sizep)
{
- unsigned long base_offset;
+ off_t base_offset;
base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
type = packed_object_info(p, base_offset, NULL);
* based on a base with a wrong size. This saves tons of
* inflate() calls.
*/
- if (sizep) {
- const unsigned char *data;
- unsigned char delta_head[20], *in;
- z_stream stream;
- int st;
-
- memset(&stream, 0, sizeof(stream));
- stream.next_out = delta_head;
- stream.avail_out = sizeof(delta_head);
-
- inflateInit(&stream);
- do {
- in = use_pack(p, w_curs, curpos, &stream.avail_in);
- stream.next_in = in;
- st = inflate(&stream, Z_FINISH);
- curpos += stream.next_in - in;
- } while ((st == Z_OK || st == Z_BUF_ERROR)
- && stream.total_out < sizeof(delta_head));
- inflateEnd(&stream);
- if ((st != Z_STREAM_END) &&
- stream.total_out != sizeof(delta_head))
- die("delta data unpack-initial failed");
-
- /* Examine the initial part of the delta to figure out
- * the result size.
- */
- data = delta_head;
-
- /* ignore base size */
- get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
-
- /* Read the result size */
- *sizep = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
- }
+ if (sizep)
+ *sizep = get_size_from_delta(p, w_curs, curpos);
return type;
}
static int unpack_object_header(struct packed_git *p,
struct pack_window **w_curs,
- unsigned long *curpos,
+ off_t *curpos,
unsigned long *sizep)
{
unsigned char *base;
}
const char *packed_object_info_detail(struct packed_git *p,
- unsigned long obj_offset,
+ off_t obj_offset,
unsigned long *size,
unsigned long *store_size,
unsigned int *delta_chain_length,
unsigned char *base_sha1)
{
struct pack_window *w_curs = NULL;
- unsigned long curpos, dummy;
+ off_t curpos;
+ unsigned long dummy;
unsigned char *next_sha1;
enum object_type type;
}
}
-static int packed_object_info(struct packed_git *p, unsigned long obj_offset,
+static int packed_object_info(struct packed_git *p, off_t obj_offset,
unsigned long *sizep)
{
struct pack_window *w_curs = NULL;
- unsigned long size, curpos = obj_offset;
+ unsigned long size;
+ off_t curpos = obj_offset;
enum object_type type;
type = unpack_object_header(p, &w_curs, &curpos, &size);
static void *unpack_compressed_entry(struct packed_git *p,
struct pack_window **w_curs,
- unsigned long curpos,
+ off_t curpos,
unsigned long size)
{
int st;
return buffer;
}
+#define MAX_DELTA_CACHE (256)
+
+static size_t delta_base_cached;
+
+static struct delta_base_cache_lru_list {
+ struct delta_base_cache_lru_list *prev;
+ struct delta_base_cache_lru_list *next;
+} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru };
+
+static struct delta_base_cache_entry {
+ struct delta_base_cache_lru_list lru;
+ void *data;
+ struct packed_git *p;
+ off_t base_offset;
+ unsigned long size;
+ enum object_type type;
+} delta_base_cache[MAX_DELTA_CACHE];
+
+static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
+{
+ unsigned long hash;
+
+ hash = (unsigned long)p + (unsigned long)base_offset;
+ hash += (hash >> 8) + (hash >> 16);
+ return hash % MAX_DELTA_CACHE;
+}
+
+static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
+ unsigned long *base_size, enum object_type *type, int keep_cache)
+{
+ void *ret;
+ unsigned long hash = pack_entry_hash(p, base_offset);
+ struct delta_base_cache_entry *ent = delta_base_cache + hash;
+
+ ret = ent->data;
+ if (ret && ent->p == p && ent->base_offset == base_offset)
+ goto found_cache_entry;
+ return unpack_entry(p, base_offset, type, base_size);
+
+found_cache_entry:
+ if (!keep_cache) {
+ ent->data = NULL;
+ ent->lru.next->prev = ent->lru.prev;
+ ent->lru.prev->next = ent->lru.next;
+ delta_base_cached -= ent->size;
+ }
+ else {
+ ret = xmalloc(ent->size + 1);
+ memcpy(ret, ent->data, ent->size);
+ ((char *)ret)[ent->size] = 0;
+ }
+ *type = ent->type;
+ *base_size = ent->size;
+ return ret;
+}
+
+static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
+{
+ if (ent->data) {
+ free(ent->data);
+ ent->data = NULL;
+ ent->lru.next->prev = ent->lru.prev;
+ ent->lru.prev->next = ent->lru.next;
+ delta_base_cached -= ent->size;
+ }
+}
+
+static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
+ void *base, unsigned long base_size, enum object_type type)
+{
+ unsigned long hash = pack_entry_hash(p, base_offset);
+ struct delta_base_cache_entry *ent = delta_base_cache + hash;
+ struct delta_base_cache_lru_list *lru;
+
+ release_delta_base_cache(ent);
+ delta_base_cached += base_size;
+
+ for (lru = delta_base_cache_lru.next;
+ delta_base_cached > delta_base_cache_limit
+ && lru != &delta_base_cache_lru;
+ lru = lru->next) {
+ struct delta_base_cache_entry *f = (void *)lru;
+ if (f->type == OBJ_BLOB)
+ release_delta_base_cache(f);
+ }
+ for (lru = delta_base_cache_lru.next;
+ delta_base_cached > delta_base_cache_limit
+ && lru != &delta_base_cache_lru;
+ lru = lru->next) {
+ struct delta_base_cache_entry *f = (void *)lru;
+ release_delta_base_cache(f);
+ }
+
+ ent->p = p;
+ ent->base_offset = base_offset;
+ ent->type = type;
+ ent->data = base;
+ ent->size = base_size;
+ ent->lru.next = &delta_base_cache_lru;
+ ent->lru.prev = delta_base_cache_lru.prev;
+ delta_base_cache_lru.prev->next = &ent->lru;
+ delta_base_cache_lru.prev = &ent->lru;
+}
+
static void *unpack_delta_entry(struct packed_git *p,
struct pack_window **w_curs,
- unsigned long curpos,
+ off_t curpos,
unsigned long delta_size,
- unsigned long obj_offset,
+ off_t obj_offset,
enum object_type *type,
unsigned long *sizep)
{
void *delta_data, *result, *base;
- unsigned long base_size, base_offset;
+ unsigned long base_size;
+ off_t base_offset;
base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
- base = unpack_entry(p, base_offset, type, &base_size);
+ base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
if (!base)
- die("failed to read delta base object at %lu from %s",
- base_offset, p->pack_name);
+ die("failed to read delta base object"
+ " at %"PRIuMAX" from %s",
+ (uintmax_t)base_offset, p->pack_name);
delta_data = unpack_compressed_entry(p, w_curs, curpos, delta_size);
result = patch_delta(base, base_size,
if (!result)
die("failed to apply delta");
free(delta_data);
- free(base);
+ add_delta_base_cache(p, base_offset, base, base_size, *type);
return result;
}
-void *unpack_entry(struct packed_git *p, unsigned long obj_offset,
+void *unpack_entry(struct packed_git *p, off_t obj_offset,
enum object_type *type, unsigned long *sizep)
{
struct pack_window *w_curs = NULL;
- unsigned long curpos = obj_offset;
+ off_t curpos = obj_offset;
void *data;
*type = unpack_object_header(p, &w_curs, &curpos, sizep);
return data;
}
-int num_packed_objects(const struct packed_git *p)
+const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
+ uint32_t n)
{
- /* See check_packed_git_idx() */
- return (p->index_size - 20 - 20 - 4*256) / 24;
+ const unsigned char *index = p->index_data;
+ if (n >= p->num_objects)
+ return NULL;
+ index += 4 * 256;
+ if (p->index_version == 1) {
+ return index + 24 * n + 4;
+ } else {
+ index += 8;
+ return index + 20 * n;
+ }
}
-int nth_packed_object_sha1(const struct packed_git *p, int n,
- unsigned char* sha1)
+static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
{
- void *index = p->index_base + 256;
- if (n < 0 || num_packed_objects(p) <= n)
- return -1;
- hashcpy(sha1, (unsigned char *) index + (24 * n) + 4);
- return 0;
+ const unsigned char *index = p->index_data;
+ index += 4 * 256;
+ if (p->index_version == 1) {
+ return ntohl(*((uint32_t *)(index + 24 * n)));
+ } else {
+ uint32_t off;
+ index += 8 + p->num_objects * (20 + 4);
+ off = ntohl(*((uint32_t *)(index + 4 * n)));
+ if (!(off & 0x80000000))
+ return off;
+ index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
+ return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
+ ntohl(*((uint32_t *)(index + 4)));
+ }
}
-unsigned long find_pack_entry_one(const unsigned char *sha1,
+off_t find_pack_entry_one(const unsigned char *sha1,
struct packed_git *p)
{
- uint32_t *level1_ofs = p->index_base;
- int hi = ntohl(level1_ofs[*sha1]);
- int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
- void *index = p->index_base + 256;
+ const uint32_t *level1_ofs = p->index_data;
+ const unsigned char *index = p->index_data;
+ unsigned hi, lo;
+
+ if (p->index_version > 1) {
+ level1_ofs += 2;
+ index += 8;
+ }
+ index += 4 * 256;
+ hi = ntohl(level1_ofs[*sha1]);
+ lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
do {
- int mi = (lo + hi) / 2;
- int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
+ unsigned mi = (lo + hi) / 2;
+ unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4);
+ int cmp = hashcmp(index + x, sha1);
if (!cmp)
- return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
+ return nth_packed_object_offset(p, mi);
if (cmp > 0)
hi = mi;
else
static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
{
struct packed_git *p;
- unsigned long offset;
+ off_t offset;
prepare_packed_git();
if (!find_pack_entry(sha1, &e, NULL))
return NULL;
else
- return unpack_entry(e.p, e.offset, type, size);
+ return cache_or_unpack_entry(e.p, e.offset, size, type, 1);
}
/*
return buf;
map = map_sha1_file(sha1, &mapsize);
if (map) {
- buf = unpack_sha1_file(map, mapsize, type, size);
+ buf = unpack_sha1_file(map, mapsize, type, size, sha1);
munmap(map, mapsize);
return buf;
}
}
}
-static void write_sha1_file_prepare(void *buf, unsigned long len,
+static void write_sha1_file_prepare(const void *buf, unsigned long len,
const char *type, unsigned char *sha1,
char *hdr, int *hdrlen)
{
stream->avail_out -= hdrlen;
}
-int hash_sha1_file(void *buf, unsigned long len, const char *type,
+int hash_sha1_file(const void *buf, unsigned long len, const char *type,
unsigned char *sha1)
{
char hdr[32];
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
- int size;
+ int size, ret;
unsigned char *compressed;
z_stream stream;
unsigned char sha1[20];
return error("sha1 file %s: %s\n", filename, strerror(errno));
}
- snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+ snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
fd = mkstemp(tmpfile);
if (fd < 0) {
/* Then the data itself.. */
stream.next_in = buf;
stream.avail_in = len;
- while (deflate(&stream, Z_FINISH) == Z_OK)
- /* nothing */;
- deflateEnd(&stream);
+ ret = deflate(&stream, Z_FINISH);
+ if (ret != Z_STREAM_END)
+ die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
+
+ ret = deflateEnd(&stream);
+ if (ret != Z_OK)
+ die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+
size = stream.total_out;
if (write_buffer(fd, compressed, size) < 0)
die("unable to write sha1 file");
fchmod(fd, 0444);
- close(fd);
+ if (close(fd))
+ die("unable to write sha1 file");
free(compressed);
return move_temp_to_file(tmpfile, filename);
int ret;
SHA_CTX c;
- snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+ snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
local = mkstemp(tmpfile);
if (local < 0) {
} while (1);
inflateEnd(&stream);
- close(local);
+ fchmod(local, 0444);
+ if (close(local) != 0)
+ die("unable to write sha1 file");
SHA1_Final(real_sha1, &c);
if (ret != Z_STREAM_END) {
unlink(tmpfile);
{
char* buf = *return_buf;
unsigned long size = *return_size;
- int iret;
+ ssize_t iret;
unsigned long off = 0;
do {
int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
enum object_type type, const char *path)
{
- unsigned long size = st->st_size;
- void *buf;
+ size_t size = xsize_t(st->st_size);
+ void *buf = NULL;
int ret, re_allocated = 0;
- buf = "";
if (size)
buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
*/
if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
unsigned long nsize = size;
- char *nbuf = buf;
- if (convert_to_git(path, &nbuf, &nsize)) {
- if (size)
- munmap(buf, size);
+ char *nbuf = convert_to_git(path, buf, &nsize);
+ if (nbuf) {
+ munmap(buf, size);
size = nsize;
buf = nbuf;
re_allocated = 1;
{
int fd;
char *target;
+ size_t len;
switch (st->st_mode & S_IFMT) {
case S_IFREG:
path);
break;
case S_IFLNK:
- target = xmalloc(st->st_size+1);
- if (readlink(path, target, st->st_size+1) != st->st_size) {
+ len = xsize_t(st->st_size);
+ target = xmalloc(len + 1);
+ if (readlink(path, target, len + 1) != st->st_size) {
char *errstr = strerror(errno);
free(target);
return error("readlink(\"%s\"): %s", path,
errstr);
}
if (!write_object)
- hash_sha1_file(target, st->st_size, blob_type, sha1);
- else if (write_sha1_file(target, st->st_size, blob_type, sha1))
+ hash_sha1_file(target, len, blob_type, sha1);
+ else if (write_sha1_file(target, len, blob_type, sha1))
return error("%s: failed to insert into database",
path);
free(target);
break;
+ case S_IFDIR:
+ return resolve_gitlink_ref(path, "HEAD", sha1);
default:
return error("%s: unsupported file type", path);
}