#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 */
char *map;
size_t mapsz;
struct stat st;
- char path[PATH_MAX];
+ const char alt_file_name[] = "info/alternates";
+ /* Given that relative_base is no longer than PATH_MAX,
+ ensure that "path" has enough space to append "/", the
+ file name, "info/alternates", and a trailing NUL. */
+ char path[PATH_MAX + 1 + sizeof alt_file_name];
int fd;
- sprintf(path, "%s/info/alternates", relative_base);
+ sprintf(path, "%s/%s", relative_base, alt_file_name);
fd = open(path, O_RDONLY);
if (fd < 0)
return;
void *idx_map;
struct pack_idx_header *hdr;
size_t idx_size;
- uint32_t nr, i, *index;
+ uint32_t version, nr, i, *index;
int fd = open(path, O_RDONLY);
struct stat st;
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- /* 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.
- */
hdr = idx_map;
if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
- munmap(idx_map, idx_size);
- return error("index file %s is a newer version"
- " and is not supported by this binary"
- " (try upgrading GIT to a newer version)",
- path);
- }
+ 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++) {
uint32_t n = ntohl(index[i]);
if (n < nr) {
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) {
- munmap(idx_map, idx_size);
- 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;
+ unsigned long max_size = min_size;
+ if (nr)
+ max_size += (nr - 1)*8;
+ if (idx_size < min_size || idx_size > max_size) {
+ 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 = 1;
+ 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))
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,
static void prepare_packed_git_one(char *objdir, int local)
{
- char path[PATH_MAX];
+ /* Ensure that this buffer is large enough so that we can
+ append "/pack/" without clobbering the stack even if
+ strlen(objdir) were PATH_MAX. */
+ char path[PATH_MAX + 1 + 4 + 1 + 1];
int len;
DIR *dir;
struct dirent *de;
if (!has_extension(de->d_name, ".idx"))
continue;
+ if (len + namelen + 1 > sizeof(path))
+ continue;
+
/* Don't reopen a pack we already have. */
strcpy(path + len, de->d_name);
for (p = packed_git; p; p = p->next) {
return unpack_sha1_rest(&stream, hdr, *size, sha1);
}
+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,
off_t *curpos,
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);
* 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;
}
return data;
}
-uint32_t 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 (uint32_t)((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;
+ }
}
-const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
- uint32_t n)
+static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
{
const unsigned char *index = p->index_data;
index += 4 * 256;
- if (num_packed_objects(p) <= n)
- return NULL;
- return index + 24 * n + 4;
+ 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)));
+ }
}
off_t find_pack_entry_one(const unsigned char *sha1,
struct packed_git *p)
{
const uint32_t *level1_ofs = p->index_data;
- int hi = ntohl(level1_ofs[*sha1]);
- int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
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(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
{
char* buf = *return_buf;
unsigned long size = *return_size;
- int iret;
+ ssize_t iret;
unsigned long off = 0;
do {
*/
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;
path);
free(target);
break;
+ case S_IFDIR:
+ return resolve_gitlink_ref(path, "HEAD", sha1);
default:
return error("%s: unsupported file type", path);
}