* This handles basic git sha1 object files - packing, unpacking,
* creation etc.
*/
-#include <sys/types.h>
-#include <dirent.h>
#include "cache.h"
#include "delta.h"
#include "pack.h"
+#include "blob.h"
+#include "commit.h"
+#include "tag.h"
+#include "tree.h"
#ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
return 0;
}
+int adjust_shared_perm(const char *path)
+{
+ struct stat st;
+ int mode;
+
+ if (!shared_repository)
+ return 0;
+ if (lstat(path, &st) < 0)
+ return -1;
+ mode = st.st_mode;
+ if (mode & S_IRUSR)
+ mode |= S_IRGRP;
+ if (mode & S_IWUSR)
+ mode |= S_IWGRP;
+ if (mode & S_IXUSR)
+ mode |= S_IXGRP;
+ if (S_ISDIR(mode))
+ mode |= S_ISGID;
+ if (chmod(path, mode) < 0)
+ return -2;
+ return 0;
+}
+
int safe_create_leading_directories(char *path)
{
char *pos = path;
+ struct stat st;
+
if (*pos == '/')
pos++;
if (!pos)
break;
*pos = 0;
- if (mkdir(path, 0777) < 0)
- if (errno != EEXIST) {
+ if (!stat(path, &st)) {
+ /* path exists */
+ if (!S_ISDIR(st.st_mode)) {
*pos = '/';
- return -1;
+ return -3;
}
+ }
+ else if (mkdir(path, 0777)) {
+ *pos = '/';
+ return -1;
+ }
+ else if (adjust_shared_perm(path)) {
+ *pos = '/';
+ return -2;
+ }
*pos++ = '/';
}
return 0;
char * sha1_to_hex(const unsigned char *sha1)
{
- static char buffer[50];
+ static int bufno;
+ static char hexbuffer[4][50];
static const char hex[] = "0123456789abcdef";
- char *buf = buffer;
+ char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
int i;
for (i = 0; i < 20; i++) {
*buf++ = hex[val >> 4];
*buf++ = hex[val & 0xf];
}
+ *buf = '\0';
+
return buffer;
}
struct alternate_object_database *alt_odb_list;
static struct alternate_object_database **alt_odb_tail;
+static void read_info_alternates(const char * alternates, int depth);
+
/*
* Prepare alternate object database registry.
*
* alternate_object_database. The elements on this list come from
* non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
* environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
- * whose contents is exactly in the same format as that environment
- * variable. Its base points at a statically allocated buffer that
+ * whose contents is similar to that environment variable but can be
+ * LF separated. Its base points at a statically allocated buffer that
* contains "/the/directory/corresponding/to/.git/objects/...", while
* its name points just after the slash at the end of ".git/objects/"
* in the example above, and has enough space to hold 40-byte hex
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
-static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
- const char *relative_base)
+static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
{
- const char *cp, *last;
+ struct stat st;
+ const char *objdir = get_object_directory();
struct alternate_object_database *ent;
+ struct alternate_object_database *alt;
+ /* 43 = 40-byte + 2 '/' + terminating NUL */
+ int pfxlen = len;
+ int entlen = pfxlen + 43;
int base_len = -1;
+ if (*entry != '/' && relative_base) {
+ /* Relative alt-odb */
+ if (base_len < 0)
+ base_len = strlen(relative_base) + 1;
+ entlen += base_len;
+ pfxlen += base_len;
+ }
+ ent = xmalloc(sizeof(*ent) + entlen);
+
+ if (*entry != '/' && relative_base) {
+ memcpy(ent->base, relative_base, base_len - 1);
+ ent->base[base_len - 1] = '/';
+ memcpy(ent->base + base_len, entry, len);
+ }
+ else
+ memcpy(ent->base, entry, pfxlen);
+
+ ent->name = ent->base + pfxlen + 1;
+ ent->base[pfxlen + 3] = '/';
+ ent->base[pfxlen] = ent->base[entlen-1] = 0;
+
+ /* Detect cases where alternate disappeared */
+ if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
+ error("object directory %s does not exist; "
+ "check .git/objects/info/alternates.",
+ ent->base);
+ free(ent);
+ return -1;
+ }
+
+ /* Prevent the common mistake of listing the same
+ * thing twice, or object directory itself.
+ */
+ for (alt = alt_odb_list; alt; alt = alt->next) {
+ if (!memcmp(ent->base, alt->base, pfxlen)) {
+ free(ent);
+ return -1;
+ }
+ }
+ if (!memcmp(ent->base, objdir, pfxlen)) {
+ free(ent);
+ return -1;
+ }
+
+ /* add the alternate entry */
+ *alt_odb_tail = ent;
+ alt_odb_tail = &(ent->next);
+ ent->next = NULL;
+
+ /* recursively add alternates */
+ read_info_alternates(ent->base, depth + 1);
+
+ ent->base[pfxlen] = '/';
+
+ return 0;
+}
+
+static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
+ const char *relative_base, int depth)
+{
+ const char *cp, *last;
+
+ if (depth > 5) {
+ error("%s: ignoring alternate object stores, nesting too deep.",
+ relative_base);
+ return;
+ }
+
last = alt;
while (last < ep) {
cp = last;
last = cp + 1;
continue;
}
- for ( ; cp < ep && *cp != sep; cp++)
- ;
+ while (cp < ep && *cp != sep)
+ cp++;
if (last != cp) {
- /* 43 = 40-byte + 2 '/' + terminating NUL */
- int pfxlen = cp - last;
- int entlen = pfxlen + 43;
-
- if (*last != '/' && relative_base) {
- /* Relative alt-odb */
- if (base_len < 0)
- base_len = strlen(relative_base) + 1;
- entlen += base_len;
- pfxlen += base_len;
- }
- ent = xmalloc(sizeof(*ent) + entlen);
- *alt_odb_tail = ent;
- alt_odb_tail = &(ent->next);
- ent->next = NULL;
- if (*last != '/' && relative_base) {
- memcpy(ent->base, relative_base, base_len - 1);
- ent->base[base_len - 1] = '/';
- memcpy(ent->base + base_len,
- last, cp - last);
+ if ((*last != '/') && depth) {
+ error("%s: ignoring relative alternate object store %s",
+ relative_base, last);
+ } else {
+ link_alt_odb_entry(last, cp - last,
+ relative_base, depth);
}
- else
- memcpy(ent->base, last, pfxlen);
- ent->name = ent->base + pfxlen + 1;
- ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
- ent->base[entlen-1] = 0;
}
while (cp < ep && *cp == sep)
cp++;
}
}
-void prepare_alt_odb(void)
+static void read_info_alternates(const char * relative_base, int depth)
{
- char path[PATH_MAX];
char *map;
- int fd;
struct stat st;
- char *alt;
-
- alt = getenv(ALTERNATE_DB_ENVIRONMENT);
- if (!alt) alt = "";
-
- if (alt_odb_tail)
- return;
- alt_odb_tail = &alt_odb_list;
- link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL);
+ char path[PATH_MAX];
+ int fd;
- sprintf(path, "%s/info/alternates", get_object_directory());
+ sprintf(path, "%s/info/alternates", relative_base);
fd = open(path, O_RDONLY);
if (fd < 0)
return;
if (map == MAP_FAILED)
return;
- link_alt_odb_entries(map, map + st.st_size, '\n',
- get_object_directory());
+ link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth);
+
munmap(map, st.st_size);
}
+void prepare_alt_odb(void)
+{
+ char *alt;
+
+ alt = getenv(ALTERNATE_DB_ENVIRONMENT);
+ if (!alt) alt = "";
+
+ if (alt_odb_tail)
+ return;
+ alt_odb_tail = &alt_odb_list;
+ link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);
+
+ read_info_alternates(get_object_directory(), 0);
+}
+
static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
{
char *name = sha1_file_name(sha1);
p->pack_last_used = 0;
p->pack_use_cnt = 0;
p->pack_local = local;
- if (!get_sha1_hex(path + path_len - 40 - 4, sha1))
+ if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
memcpy(p->sha1, sha1, 20);
return p;
}
sprintf(path, "%s/pack", objdir);
len = strlen(path);
dir = opendir(path);
- if (!dir)
+ if (!dir) {
+ if (errno != ENOENT)
+ error("unable to open object pack directory: %s: %s",
+ path, strerror(errno));
return;
+ }
path[len++] = '/';
while ((de = readdir(dir)) != NULL) {
int namelen = strlen(de->d_name);
prepare_packed_git_one(get_object_directory(), 1);
prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt->next) {
- alt->name[0] = 0;
+ alt->name[-1] = 0;
prepare_packed_git_one(alt->base, 0);
+ alt->name[-1] = '/';
}
run_once = 1;
}
* the result size.
*/
data = delta_head;
- get_delta_hdr_size(&data); /* ignore base size */
+
+ /* ignore base size */
+ get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
/* Read the result size */
- result_size = get_delta_hdr_size(&data);
+ result_size = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
*sizep = result_size;
}
return 0;
return offset;
}
+int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
+ unsigned char *base, unsigned long *sizep,
+ enum object_type *kindp)
+{
+ unsigned long ptr;
+ int status = -1;
+
+ use_packed_git(p);
+ ptr = offset;
+ ptr = unpack_object_header(p, ptr, kindp, sizep);
+ if (*kindp != OBJ_DELTA)
+ goto done;
+ memcpy(base, p->pack_base + ptr, 20);
+ status = 0;
+ done:
+ unuse_packed_git(p);
+ return status;
+}
+
void packed_object_info_detail(struct pack_entry *e,
char *type,
unsigned long *size,
unsigned long *store_size,
- int *delta_chain_length,
+ unsigned int *delta_chain_length,
unsigned char *base_sha1)
{
struct packed_git *p = e->p;
- unsigned long offset, left;
+ unsigned long offset;
unsigned char *pack;
enum object_type kind;
offset = unpack_object_header(p, e->offset, &kind, size);
pack = p->pack_base + offset;
- left = p->pack_size - offset;
if (kind != OBJ_DELTA)
*delta_chain_length = 0;
else {
- int chain_length = 0;
+ unsigned int chain_length = 0;
+ if (p->pack_size <= offset + 20)
+ die("pack file %s records an incomplete delta base",
+ p->pack_name);
memcpy(base_sha1, pack, 20);
do {
struct pack_entry base_ent;
}
switch (kind) {
case OBJ_COMMIT:
- strcpy(type, "commit");
+ strcpy(type, commit_type);
break;
case OBJ_TREE:
- strcpy(type, "tree");
+ strcpy(type, tree_type);
break;
case OBJ_BLOB:
- strcpy(type, "blob");
+ strcpy(type, blob_type);
break;
case OBJ_TAG:
- strcpy(type, "tag");
+ strcpy(type, tag_type);
break;
default:
die("corrupted pack file %s containing object of kind %d",
unuse_packed_git(p);
return retval;
case OBJ_COMMIT:
- strcpy(type, "commit");
+ strcpy(type, commit_type);
break;
case OBJ_TREE:
- strcpy(type, "tree");
+ strcpy(type, tree_type);
break;
case OBJ_BLOB:
- strcpy(type, "blob");
+ strcpy(type, blob_type);
break;
case OBJ_TAG:
- strcpy(type, "tag");
+ strcpy(type, tag_type);
break;
default:
die("corrupted pack file %s containing object of kind %d",
if (left < 20)
die("truncated pack file");
+
+ /* The base entry _must_ be in the same pack */
+ if (!find_pack_entry_one(base_sha1, &base_ent, p))
+ die("failed to find delta-pack base object %s",
+ sha1_to_hex(base_sha1));
+ base = unpack_entry_gently(&base_ent, type, &base_size);
+ if (!base)
+ die("failed to read delta-pack base object %s",
+ sha1_to_hex(base_sha1));
+
data = base_sha1 + 20;
data_size = left - 20;
delta_data = xmalloc(delta_size);
if ((st != Z_STREAM_END) || stream.total_out != delta_size)
die("delta data unpack failed");
- /* The base entry _must_ be in the same pack */
- if (!find_pack_entry_one(base_sha1, &base_ent, p))
- die("failed to find delta-pack base object %s",
- sha1_to_hex(base_sha1));
- base = unpack_entry_gently(&base_ent, type, &base_size);
- if (!base)
- die("failed to read delta-pack base object %s",
- sha1_to_hex(base_sha1));
result = patch_delta(base, base_size,
delta_data, delta_size,
&result_size);
retval = unpack_delta_entry(pack, size, left, type, sizep, p);
return retval;
case OBJ_COMMIT:
- strcpy(type, "commit");
+ strcpy(type, commit_type);
break;
case OBJ_TREE:
- strcpy(type, "tree");
+ strcpy(type, tree_type);
break;
case OBJ_BLOB:
- strcpy(type, "blob");
+ strcpy(type, blob_type);
break;
case OBJ_TAG:
- strcpy(type, "tag");
+ strcpy(type, tag_type);
break;
default:
return NULL;
int mi = (lo + hi) / 2;
int cmp = memcmp(index + 24 * mi + 4, sha1, 20);
if (!cmp) {
- e->offset = ntohl(*((int*)(index + 24 * mi)));
+ e->offset = ntohl(*((unsigned int *)(index + 24 * mi)));
memcpy(e->sha1, sha1, 20);
e->p = p;
return 1;
return buffer;
}
/* Handle references */
- else if (!strcmp(type, "commit"))
+ else if (!strcmp(type, commit_type))
ref_type = "tree ";
- else if (!strcmp(type, "tag"))
+ else if (!strcmp(type, tag_type))
ref_type = "object ";
else {
free(buffer);
if (dir) {
*dir = 0;
mkdir(filename, 0777);
+ if (adjust_shared_perm(filename))
+ return -2;
*dir = '/';
if (!link(tmpfile, filename))
return 0;
unlink(tmpfile);
if (ret) {
if (ret != EEXIST) {
- fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret));
+ fprintf(stderr, "unable to write sha1 filename %s: %s\n", filename, strerror(ret));
return -1;
}
/* FIXME!!! Collision check here ? */
return 0;
}
+static int write_buffer(int fd, const void *buf, size_t len)
+{
+ while (len) {
+ ssize_t size;
+
+ size = write(fd, buf, len);
+ if (!size)
+ return error("file write: disk full");
+ if (size < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return error("file write error (%s)", strerror(errno));
+ }
+ len -= size;
+ buf += size;
+ }
+ return 0;
+}
+
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
int size;
}
if (errno != ENOENT) {
- fprintf(stderr, "sha1 file %s: %s", filename, strerror(errno));
+ fprintf(stderr, "sha1 file %s: %s\n", filename, strerror(errno));
return -1;
}
fd = mkstemp(tmpfile);
if (fd < 0) {
- fprintf(stderr, "unable to create temporary sha1 filename %s: %s", tmpfile, strerror(errno));
+ fprintf(stderr, "unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno));
return -1;
}
deflateEnd(&stream);
size = stream.total_out;
- if (write(fd, compressed, size) != size)
- die("unable to write file");
+ if (write_buffer(fd, compressed, size) < 0)
+ die("unable to write sha1 file");
fchmod(fd, 0444);
close(fd);
free(compressed);
return move_temp_to_file(tmpfile, filename);
}
-int write_sha1_to_fd(int fd, const unsigned char *sha1)
+/*
+ * We need to unpack and recompress the object for writing
+ * it out to a different file.
+ */
+static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
{
- ssize_t size;
- unsigned long objsize;
- int posn = 0;
- void *map = map_sha1_file_internal(sha1, &objsize);
- void *buf = map;
- void *temp_obj = NULL;
+ size_t size;
z_stream stream;
+ unsigned char *unpacked;
+ unsigned long len;
+ char type[20];
+ char hdr[50];
+ int hdrlen;
+ void *buf;
- if (!buf) {
- unsigned char *unpacked;
- unsigned long len;
- char type[20];
- char hdr[50];
- int hdrlen;
- // need to unpack and recompress it by itself
- unpacked = read_packed_sha1(sha1, type, &len);
+ // need to unpack and recompress it by itself
+ unpacked = read_packed_sha1(sha1, type, &len);
- hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
+ hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
- /* Set it up */
- memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, Z_BEST_COMPRESSION);
- size = deflateBound(&stream, len + hdrlen);
- temp_obj = buf = xmalloc(size);
-
- /* Compress it */
- stream.next_out = buf;
- stream.avail_out = size;
-
- /* First header.. */
- stream.next_in = (void *)hdr;
- stream.avail_in = hdrlen;
- while (deflate(&stream, 0) == Z_OK)
- /* nothing */;
+ /* Set it up */
+ memset(&stream, 0, sizeof(stream));
+ deflateInit(&stream, Z_BEST_COMPRESSION);
+ size = deflateBound(&stream, len + hdrlen);
+ buf = xmalloc(size);
- /* Then the data itself.. */
- stream.next_in = unpacked;
- stream.avail_in = len;
- while (deflate(&stream, Z_FINISH) == Z_OK)
- /* nothing */;
- deflateEnd(&stream);
- free(unpacked);
-
- objsize = stream.total_out;
- }
+ /* Compress it */
+ stream.next_out = buf;
+ stream.avail_out = size;
- do {
- size = write(fd, buf + posn, objsize - posn);
- if (size <= 0) {
- if (!size) {
- fprintf(stderr, "write closed");
- } else {
- perror("write ");
- }
- return -1;
- }
- posn += size;
- } while (posn < objsize);
+ /* First header.. */
+ stream.next_in = (void *)hdr;
+ stream.avail_in = hdrlen;
+ while (deflate(&stream, 0) == Z_OK)
+ /* nothing */;
+
+ /* Then the data itself.. */
+ stream.next_in = unpacked;
+ stream.avail_in = len;
+ while (deflate(&stream, Z_FINISH) == Z_OK)
+ /* nothing */;
+ deflateEnd(&stream);
+ free(unpacked);
- if (map)
- munmap(map, objsize);
- if (temp_obj)
- free(temp_obj);
+ *objsize = stream.total_out;
+ return buf;
+}
- return 0;
+int write_sha1_to_fd(int fd, const unsigned char *sha1)
+{
+ int retval;
+ unsigned long objsize;
+ void *buf = map_sha1_file_internal(sha1, &objsize);
+
+ if (buf) {
+ retval = write_buffer(fd, buf, objsize);
+ munmap(buf, objsize);
+ return retval;
+ }
+
+ buf = repack_object(sha1, &objsize);
+ retval = write_buffer(fd, buf, objsize);
+ free(buf);
+ return retval;
}
int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
local = mkstemp(tmpfile);
if (local < 0)
- return error("Couldn't open %s for %s\n", tmpfile, sha1_to_hex(sha1));
+ return error("Couldn't open %s for %s",
+ tmpfile, sha1_to_hex(sha1));
memset(&stream, 0, sizeof(stream));
SHA1_Update(&c, discard, sizeof(discard) -
stream.avail_out);
} while (stream.avail_in && ret == Z_OK);
- write(local, buffer, *bufposn - stream.avail_in);
+ if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0)
+ die("unable to write sha1 file");
memmove(buffer, buffer + *bufposn - stream.avail_in,
stream.avail_in);
*bufposn = stream.avail_in;
}
if (memcmp(sha1, real_sha1, 20)) {
unlink(tmpfile);
- return error("File %s has bad hash\n", sha1_to_hex(sha1));
+ return error("File %s has bad hash", sha1_to_hex(sha1));
}
return move_temp_to_file(tmpfile, sha1_file_name(sha1));
return find_sha1_file(sha1, &st) ? 1 : 0;
}
+/*
+ * reads from fd as long as possible into a supplied buffer of size bytes.
+ * If neccessary the buffer's size is increased using realloc()
+ *
+ * returns 0 if anything went fine and -1 otherwise
+ *
+ * NOTE: both buf and size may change, but even when -1 is returned
+ * you still have to free() it yourself.
+ */
+int read_pipe(int fd, char** return_buf, unsigned long* return_size)
+{
+ char* buf = *return_buf;
+ unsigned long size = *return_size;
+ int iret;
+ unsigned long off = 0;
+
+ do {
+ iret = xread(fd, buf + off, size - off);
+ if (iret > 0) {
+ off += iret;
+ if (off == size) {
+ size *= 2;
+ buf = realloc(buf, size);
+ }
+ }
+ } while (iret > 0);
+
+ *return_buf = buf;
+ *return_size = off;
+
+ if (iret < 0)
+ return -1;
+ return 0;
+}
+
+int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+{
+ unsigned long size = 4096;
+ char *buf = malloc(size);
+ int ret;
+ unsigned char hdr[50];
+ int hdrlen;
+
+ if (read_pipe(fd, &buf, &size)) {
+ free(buf);
+ return -1;
+ }
+
+ if (!type)
+ type = blob_type;
+ if (write_object)
+ ret = write_sha1_file(buf, size, type, sha1);
+ else {
+ write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
+ ret = 0;
+ }
+ free(buf);
+ return ret;
+}
+
int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type)
{
unsigned long size = st->st_size;
return -1;
if (!type)
- type = "blob";
+ type = blob_type;
if (write_object)
ret = write_sha1_file(buf, size, type, sha1);
else {
if (!write_object) {
unsigned char hdr[50];
int hdrlen;
- write_sha1_file_prepare(target, st->st_size, "blob",
+ write_sha1_file_prepare(target, st->st_size, blob_type,
sha1, hdr, &hdrlen);
- } else if (write_sha1_file(target, st->st_size, "blob", sha1))
+ } else if (write_sha1_file(target, st->st_size, blob_type, sha1))
return error("%s: failed to insert into database",
path);
free(target);