* 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"
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;
*buf++ = hex[val >> 4];
*buf++ = hex[val & 0xf];
}
+ *buf = '\0';
+
return buffer;
}
* 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
{
const char *cp, *last;
struct alternate_object_database *ent;
+ const char *objdir = get_object_directory();
int base_len = -1;
last = alt;
for ( ; cp < ep && *cp != sep; cp++)
;
if (last != cp) {
+ struct stat st;
+ struct alternate_object_database *alt;
/* 43 = 40-byte + 2 '/' + terminating NUL */
int pfxlen = cp - last;
int entlen = pfxlen + 43;
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] = '/';
}
else
memcpy(ent->base, last, pfxlen);
+
ent->name = ent->base + pfxlen + 1;
- ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
- ent->base[entlen-1] = 0;
+ 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);
+ goto bad;
+ }
+ ent->base[pfxlen] = '/';
+
+ /* 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))
+ goto bad;
+ if (!memcmp(ent->base, objdir, pfxlen)) {
+ bad:
+ free(ent);
+ }
+ else {
+ *alt_odb_tail = ent;
+ alt_odb_tail = &(ent->next);
+ ent->next = NULL;
+ }
}
while (cp < ep && *cp == sep)
cp++;
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;
}
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;
if (kind != OBJ_DELTA)
*delta_chain_length = 0;
else {
- int chain_length = 0;
+ unsigned int chain_length = 0;
memcpy(base_sha1, pack, 20);
do {
struct pack_entry base_ent;
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);
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 ? */
}
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;
}
size = write(fd, buf + posn, objsize - posn);
if (size <= 0) {
if (!size) {
- fprintf(stderr, "write closed");
+ fprintf(stderr, "write closed\n");
} else {
perror("write ");
}
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));
}
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;
}
+int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+{
+ unsigned long size = 4096;
+ char *buf = malloc(size);
+ int iret, ret;
+ unsigned long off = 0;
+ unsigned char hdr[50];
+ int hdrlen;
+ do {
+ iret = read(fd, buf + off, size - off);
+ if (iret > 0) {
+ off += iret;
+ if (off == size) {
+ size *= 2;
+ buf = realloc(buf, size);
+ }
+ }
+ } while (iret > 0);
+ if (iret < 0) {
+ free(buf);
+ return -1;
+ }
+ if (!type)
+ type = "blob";
+ if (write_object)
+ ret = write_sha1_file(buf, off, type, sha1);
+ else {
+ write_sha1_file_prepare(buf, off, 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;