Merge branch 'jc/repack'
[gitweb.git] / sha1_file.c
index 3372ebcdcac0d36b81663f958784e8a1ec84b900..817963045b81cef36bf3a9c76b7399a70226a181 100644 (file)
@@ -13,7 +13,6 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
-#include <stdint.h>
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -51,29 +50,6 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)
        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;
@@ -367,7 +343,7 @@ static void read_info_alternates(const char * relative_base, int depth)
 
 void prepare_alt_odb(void)
 {
-       char *alt;
+       const char *alt;
 
        alt = getenv(ALTERNATE_DB_ENVIRONMENT);
        if (!alt) alt = "";
@@ -510,8 +486,9 @@ int use_packed_git(struct packed_git *p)
                 * this is cheap.
                 */
                if (memcmp((char*)(p->index_base) + p->index_size - 40,
-                          p->pack_base + p->pack_size - 20, 20)) {
-                             
+                          (char *) p->pack_base + p->pack_size - 20,
+                          20)) {
+
                        die("packfile %s does not match index.", p->pack_name);
                }
        }
@@ -618,6 +595,12 @@ static void prepare_packed_git_one(char *objdir, int local)
 
                /* we have .idx.  Is it a file we can map? */
                strcpy(path + len, de->d_name);
+               for (p = packed_git; p; p = p->next) {
+                       if (!memcmp(path, p->pack_name, len + namelen - 4))
+                               break;
+               }
+               if (p)
+                       continue;
                p = add_packed_git(path, len + namelen, local);
                if (!p)
                        continue;
@@ -627,12 +610,12 @@ static void prepare_packed_git_one(char *objdir, int local)
        closedir(dir);
 }
 
+static int prepare_packed_git_run_once = 0;
 void prepare_packed_git(void)
 {
-       static int run_once = 0;
        struct alternate_object_database *alt;
 
-       if (run_once)
+       if (prepare_packed_git_run_once)
                return;
        prepare_packed_git_one(get_object_directory(), 1);
        prepare_alt_odb();
@@ -641,7 +624,13 @@ void prepare_packed_git(void)
                prepare_packed_git_one(alt->base, 0);
                alt->name[-1] = '/';
        }
-       run_once = 1;
+       prepare_packed_git_run_once = 1;
+}
+
+static void reprepare_packed_git(void)
+{
+       prepare_packed_git_run_once = 0;
+       prepare_packed_git();
 }
 
 int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
@@ -713,7 +702,7 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmalloc(1+size);
 
-       memcpy(buf, buffer + bytes, stream->total_out - bytes);
+       memcpy(buf, (char *) buffer + bytes, stream->total_out - bytes);
        bytes = stream->total_out - bytes;
        if (bytes < size) {
                stream->next_out = buf + bytes;
@@ -865,7 +854,7 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
        if (offset >= p->pack_size)
                die("object offset outside of pack file");
 
-       pack =  p->pack_base + offset;
+       pack =  (unsigned char *) p->pack_base + offset;
        c = *pack++;
        offset++;
        *type = (c >> 4) & 7;
@@ -895,7 +884,7 @@ int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
        ptr = unpack_object_header(p, ptr, kindp, sizep);
        if (*kindp != OBJ_DELTA)
                goto done;
-       memcpy(base, p->pack_base + ptr, 20);
+       memcpy(base, (char *) p->pack_base + ptr, 20);
        status = 0;
  done:
        unuse_packed_git(p);
@@ -915,7 +904,7 @@ void packed_object_info_detail(struct pack_entry *e,
        enum object_type kind;
 
        offset = unpack_object_header(p, e->offset, &kind, size);
-       pack = p->pack_base + offset;
+       pack = (unsigned char *) p->pack_base + offset;
        if (kind != OBJ_DELTA)
                *delta_chain_length = 0;
        else {
@@ -931,7 +920,7 @@ void packed_object_info_detail(struct pack_entry *e,
                        find_pack_entry_one(pack, &base_ent, p);
                        offset = unpack_object_header(p, base_ent.offset,
                                                      &kind, &junk);
-                       pack = p->pack_base + offset;
+                       pack = (unsigned char *) p->pack_base + offset;
                        chain_length++;
                } while (kind == OBJ_DELTA);
                *delta_chain_length = chain_length;
@@ -969,7 +958,7 @@ static int packed_object_info(struct pack_entry *entry,
                die("cannot map packed file");
 
        offset = unpack_object_header(p, entry->offset, &kind, &size);
-       pack = p->pack_base + offset;
+       pack = (unsigned char *) p->pack_base + offset;
        left = p->pack_size - offset;
 
        switch (kind) {
@@ -1108,7 +1097,7 @@ void *unpack_entry_gently(struct pack_entry *entry,
        void *retval;
 
        offset = unpack_object_header(p, entry->offset, &kind, &size);
-       pack = p->pack_base + offset;
+       pack = (unsigned char *) p->pack_base + offset;
        left = p->pack_size - offset;
        switch (kind) {
        case OBJ_DELTA:
@@ -1146,7 +1135,7 @@ int nth_packed_object_sha1(const struct packed_git *p, int n,
        void *index = p->index_base + 256;
        if (n < 0 || num_packed_objects(p) <= n)
                return -1;
-       memcpy(sha1, (index + 24 * n + 4), 20);
+       memcpy(sha1, (char *) index + (24 * n) + 4, 20);
        return 0;
 }
 
@@ -1160,9 +1149,9 @@ int find_pack_entry_one(const unsigned char *sha1,
 
        do {
                int mi = (lo + hi) / 2;
-               int cmp = memcmp(index + 24 * mi + 4, sha1, 20);
+               int cmp = memcmp((char *) index + (24 * mi) + 4, sha1, 20);
                if (!cmp) {
-                       e->offset = ntohl(*((uint32_t *)(index + 24 * mi)));
+                       e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
                        memcpy(e->sha1, sha1, 20);
                        e->p = p;
                        return 1;
@@ -1213,9 +1202,12 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
        if (!map) {
                struct pack_entry e;
 
-               if (!find_pack_entry(sha1, &e))
-                       return error("unable to find %s", sha1_to_hex(sha1));
-               return packed_object_info(&e, type, sizep);
+               if (find_pack_entry(sha1, &e))
+                       return packed_object_info(&e, type, sizep);
+               reprepare_packed_git();
+               if (find_pack_entry(sha1, &e))
+                       return packed_object_info(&e, type, sizep);
+               return error("unable to find %s", sha1_to_hex(sha1));
        }
        if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
@@ -1257,6 +1249,9 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
                munmap(map, mapsize);
                return buf;
        }
+       reprepare_packed_git();
+       if (find_pack_entry(sha1, &e))
+               return read_packed_sha1(sha1, type, size);
        return NULL;
 }
 
@@ -1296,7 +1291,7 @@ void *read_object_with_reference(const unsigned char *sha1,
                ref_length = strlen(ref_type);
 
                if (memcmp(buffer, ref_type, ref_length) ||
-                   get_sha1_hex(buffer + ref_length, actual_sha1)) {
+                   get_sha1_hex((char *) buffer + ref_length, actual_sha1)) {
                        free(buffer);
                        return NULL;
                }
@@ -1400,6 +1395,25 @@ int move_temp_to_file(const char *tmpfile, char *filename)
        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 = (char *) buf + size;
+       }
+       return 0;
+}
+
 int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
        int size;
@@ -1466,8 +1480,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        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);
@@ -1475,73 +1489,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        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\n");
-                       } 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 */;
 
-       if (map)
-               munmap(map, objsize);
-       if (temp_obj)
-               free(temp_obj);
+       /* Then the data itself.. */
+       stream.next_in = unpacked;
+       stream.avail_in = len;
+       while (deflate(&stream, Z_FINISH) == Z_OK)
+               /* nothing */;
+       deflateEnd(&stream);
+       free(unpacked);
 
-       return 0;
+       *objsize = stream.total_out;
+       return buf;
+}
+
+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,
@@ -1580,7 +1591,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
                                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;
@@ -1646,16 +1658,24 @@ int has_sha1_file(const unsigned char *sha1)
        return find_sha1_file(sha1, &st) ? 1 : 0;
 }
 
-int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+/*
+ * 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)
 {
-       unsigned long size = 4096;
-       char *buf = malloc(size);
-       int iret, ret;
+       char* buf = *return_buf;
+       unsigned long size = *return_size;
+       int iret;
        unsigned long off = 0;
-       unsigned char hdr[50];
-       int hdrlen;
+
        do {
-               iret = read(fd, buf + off, size - off);
+               iret = xread(fd, buf + off, size - off);
                if (iret > 0) {
                        off += iret;
                        if (off == size) {
@@ -1664,16 +1684,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
                        }
                }
        } while (iret > 0);
-       if (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, off, type, sha1);
+               ret = write_sha1_file(buf, size, type, sha1);
        else {
-               write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen);
+               write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
                ret = 0;
        }
        free(buf);