gitview.txt: improve asciidoc markup
[gitweb.git] / sha1_file.c
index bc3580844023c9ae0f833ea5799c05887e1d88aa..066cff1fa6e8dd1485876984becdecd7bb4987b6 100644 (file)
@@ -22,7 +22,7 @@
 #endif
 #endif
 
-const unsigned char null_sha1[20] = { 0, };
+const unsigned char null_sha1[20];
 
 static unsigned int sha1_file_open_flag = O_NOATIME;
 
@@ -453,7 +453,7 @@ int use_packed_git(struct packed_git *p)
 {
        if (!p->pack_size) {
                struct stat st;
-               // We created the struct before we had the pack
+               /* We created the struct before we had the pack */
                stat(p->pack_name, &st);
                if (!S_ISREG(st.st_mode))
                        die("packfile %s not a regular file", p->pack_name);
@@ -463,6 +463,7 @@ int use_packed_git(struct packed_git *p)
                int fd;
                struct stat st;
                void *map;
+               struct pack_header *hdr;
 
                pack_mapped += p->pack_size;
                while (PACK_MAX_SZ < pack_mapped && unuse_one_packed_git())
@@ -482,13 +483,24 @@ int use_packed_git(struct packed_git *p)
                        die("packfile %s cannot be mapped.", p->pack_name);
                p->pack_base = map;
 
+               /* Check if we understand this pack file.  If we don't we're
+                * likely too old to handle it.
+                */
+               hdr = map;
+               if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
+                       die("packfile %s isn't actually a pack.", p->pack_name);
+               if (!pack_version_ok(hdr->hdr_version))
+                       die("packfile %s is version %i and not supported"
+                               " (try upgrading GIT to a newer version)",
+                               p->pack_name, ntohl(hdr->hdr_version));
+
                /* Check if the pack file matches with the index file.
                 * this is cheap.
                 */
-               if (memcmp((char*)(p->index_base) + p->index_size - 40,
-                          (char *) p->pack_base + p->pack_size - 20,
-                          20)) {
-
+               if (hashcmp((unsigned char *)(p->index_base) +
+                           p->index_size - 40,
+                           (unsigned char *)p->pack_base +
+                           p->pack_size - 20)) {
                        die("packfile %s does not match index.", p->pack_name);
                }
        }
@@ -590,7 +602,7 @@ static void prepare_packed_git_one(char *objdir, int local)
                int namelen = strlen(de->d_name);
                struct packed_git *p;
 
-               if (strcmp(de->d_name + namelen - 4, ".idx"))
+               if (!has_extension(de->d_name, ".idx"))
                        continue;
 
                /* we have .idx.  Is it a file we can map? */
@@ -643,11 +655,10 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz
        SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size));
        SHA1_Update(&c, map, size);
        SHA1_Final(real_sha1, &c);
-       return memcmp(sha1, real_sha1, 20) ? -1 : 0;
+       return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
-static void *map_sha1_file_internal(const unsigned char *sha1,
-                                   unsigned long *size)
+void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
 {
        struct stat st;
        void *map;
@@ -684,26 +695,84 @@ static void *map_sha1_file_internal(const unsigned char *sha1,
        return map;
 }
 
-int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size)
+int legacy_loose_object(unsigned char *map)
+{
+       unsigned int word;
+
+       /*
+        * Is it a zlib-compressed buffer? If so, the first byte
+        * must be 0x78 (15-bit window size, deflated), and the
+        * first 16-bit word is evenly divisible by 31
+        */
+       word = (map[0] << 8) + map[1];
+       if (map[0] == 0x78 && !(word % 31))
+               return 1;
+       else
+               return 0;
+}
+
+static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 {
+       unsigned char c;
+       unsigned int bits;
+       unsigned long size;
+       static const char *typename[8] = {
+               NULL,   /* OBJ_EXT */
+               "commit", "tree", "blob", "tag",
+               NULL, NULL, NULL
+       };
+       const char *type;
+
        /* Get the data stream */
        memset(stream, 0, sizeof(*stream));
        stream->next_in = map;
        stream->avail_in = mapsize;
        stream->next_out = buffer;
-       stream->avail_out = size;
+       stream->avail_out = bufsiz;
+
+       if (legacy_loose_object(map)) {
+               inflateInit(stream);
+               return inflate(stream, 0);
+       }
+
+       c = *map++;
+       mapsize--;
+       type = typename[(c >> 4) & 7];
+       if (!type)
+               return -1;
+
+       bits = 4;
+       size = c & 0xf;
+       while ((c & 0x80)) {
+               if (bits >= 8*sizeof(long))
+                       return -1;
+               c = *map++;
+               size += (c & 0x7f) << bits;
+               bits += 7;
+               mapsize--;
+       }
 
+       /* Set up the stream for the rest.. */
+       stream->next_in = map;
+       stream->avail_in = mapsize;
        inflateInit(stream);
-       return inflate(stream, 0);
+
+       /* And generate the fake traditional header */
+       stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size);
+       return 0;
 }
 
 static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
 {
        int bytes = strlen(buffer) + 1;
        unsigned char *buf = xmalloc(1+size);
+       unsigned long n;
 
-       memcpy(buf, (char *) buffer + bytes, stream->total_out - bytes);
-       bytes = stream->total_out - bytes;
+       n = stream->total_out - bytes;
+       if (n > size)
+               n = size;
+       memcpy(buf, (char *) buffer + bytes, n);
+       bytes = n;
        if (bytes < size) {
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
@@ -720,7 +789,7 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
  * too permissive for what we want to check. So do an anal
  * object header parse by hand.
  */
-int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
+static int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
 {
        int i;
        unsigned long size;
@@ -884,7 +953,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, (char *) p->pack_base + ptr, 20);
+       memcpy(base, (unsigned char *) p->pack_base + ptr, 20);
        status = 0;
  done:
        unuse_packed_git(p);
@@ -988,9 +1057,6 @@ static int packed_object_info(struct pack_entry *entry,
        return 0;
 }
 
-/* forward declaration for a mutually recursive function */
-static void *unpack_entry(struct pack_entry *, char *, unsigned long *);
-
 static void *unpack_delta_entry(unsigned char *base_sha1,
                                unsigned long delta_size,
                                unsigned long left,
@@ -1149,7 +1215,7 @@ int find_pack_entry_one(const unsigned char *sha1,
 
        do {
                int mi = (lo + hi) / 2;
-               int cmp = memcmp((char *) index + (24 * mi) + 4, sha1, 20);
+               int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1);
                if (!cmp) {
                        e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
                        memcpy(e->sha1, sha1, 20);
@@ -1198,7 +1264,7 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
        z_stream stream;
        char hdr[128];
 
-       map = map_sha1_file_internal(sha1, &mapsize);
+       map = map_sha1_file(sha1, &mapsize);
        if (!map) {
                struct pack_entry e;
 
@@ -1243,7 +1309,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
 
        if (find_pack_entry(sha1, &e))
                return read_packed_sha1(sha1, type, size);
-       map = map_sha1_file_internal(sha1, &mapsize);
+       map = map_sha1_file(sha1, &mapsize);
        if (map) {
                buf = unpack_sha1_file(map, mapsize, type, size);
                munmap(map, mapsize);
@@ -1331,31 +1397,29 @@ char *write_sha1_file_prepare(void *buf,
 static int link_temp_to_file(const char *tmpfile, char *filename)
 {
        int ret;
+       char *dir;
 
        if (!link(tmpfile, filename))
                return 0;
 
        /*
-        * Try to mkdir the last path component if that failed
-        * with an ENOENT.
+        * Try to mkdir the last path component if that failed.
         *
         * Re-try the "link()" regardless of whether the mkdir
         * succeeds, since a race might mean that somebody
         * else succeeded.
         */
        ret = errno;
-       if (ret == ENOENT) {
-               char *dir = strrchr(filename, '/');
-               if (dir) {
-                       *dir = 0;
-                       mkdir(filename, 0777);
-                       if (adjust_shared_perm(filename))
-                               return -2;
-                       *dir = '/';
-                       if (!link(tmpfile, filename))
-                               return 0;
-                       ret = errno;
-               }
+       dir = strrchr(filename, '/');
+       if (dir) {
+               *dir = 0;
+               mkdir(filename, 0777);
+               if (adjust_shared_perm(filename))
+                       return -2;
+               *dir = '/';
+               if (!link(tmpfile, filename))
+                       return 0;
+               ret = errno;
        }
        return ret;
 }
@@ -1414,6 +1478,49 @@ static int write_buffer(int fd, const void *buf, size_t len)
        return 0;
 }
 
+static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len)
+{
+       int hdr_len;
+       unsigned char c;
+
+       c = (type << 4) | (len & 15);
+       len >>= 4;
+       hdr_len = 1;
+       while (len) {
+               *hdr++ = c | 0x80;
+               hdr_len++;
+               c = (len & 0x7f);
+               len >>= 7;
+       }
+       *hdr = c;
+       return hdr_len;
+}
+
+static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
+{
+       int obj_type, hdr;
+
+       if (use_legacy_headers) {
+               while (deflate(stream, 0) == Z_OK)
+                       /* nothing */;
+               return;
+       }
+       if (!strcmp(type, blob_type))
+               obj_type = OBJ_BLOB;
+       else if (!strcmp(type, tree_type))
+               obj_type = OBJ_TREE;
+       else if (!strcmp(type, commit_type))
+               obj_type = OBJ_COMMIT;
+       else if (!strcmp(type, tag_type))
+               obj_type = OBJ_TAG;
+       else
+               die("trying to generate bogus object of type '%s'", type);
+       hdr = write_binary_header(stream->next_out, obj_type, len);
+       stream->total_out = hdr;
+       stream->next_out += hdr;
+       stream->avail_out -= hdr;
+}
+
 int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
        int size;
@@ -1459,7 +1566,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
        deflateInit(&stream, zlib_compression_level);
-       size = deflateBound(&stream, len+hdrlen);
+       size = 8 + deflateBound(&stream, len+hdrlen);
        compressed = xmalloc(size);
 
        /* Compress it */
@@ -1469,8 +1576,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* First header.. */
        stream.next_in = hdr;
        stream.avail_in = hdrlen;
-       while (deflate(&stream, 0) == Z_OK)
-               /* nothing */;
+       setup_object_header(&stream, type, len);
 
        /* Then the data itself.. */
        stream.next_in = buf;
@@ -1504,7 +1610,7 @@ static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
        int hdrlen;
        void *buf;
 
-       // need to unpack and recompress it by itself
+       /* need to unpack and recompress it by itself */
        unpacked = read_packed_sha1(sha1, type, &len);
 
        hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
@@ -1541,7 +1647,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1)
 {
        int retval;
        unsigned long objsize;
-       void *buf = map_sha1_file_internal(sha1, &objsize);
+       void *buf = map_sha1_file(sha1, &objsize);
 
        if (buf) {
                retval = write_buffer(fd, buf, objsize);
@@ -1618,7 +1724,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
                unlink(tmpfile);
                return error("File %s corrupted", sha1_to_hex(sha1));
        }
-       if (memcmp(sha1, real_sha1, 20)) {
+       if (hashcmp(sha1, real_sha1)) {
                unlink(tmpfile);
                return error("File %s has bad hash", sha1_to_hex(sha1));
        }
@@ -1660,7 +1766,7 @@ int has_sha1_file(const unsigned char *sha1)
 
 /*
  * reads from fd as long as possible into a supplied buffer of size bytes.
- * If neccessary the buffer's size is increased using realloc()
+ * If necessary the buffer's size is increased using realloc()
  *
  * returns 0 if anything went fine and -1 otherwise
  *