Merge branch 'jc/waitpid'
[gitweb.git] / sha1_file.c
index 54648282592d85cea0b1d42e02f358c240991ce2..c80528b506e98c1e1dae463fa08159677a2a9473 100644 (file)
@@ -50,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;
@@ -217,6 +194,8 @@ char *sha1_pack_index_name(const unsigned char *sha1)
 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.
  *
@@ -232,14 +211,85 @@ static struct alternate_object_database **alt_odb_tail;
  * 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 alternate_object_database *ent;
+       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;
@@ -249,60 +299,15 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
                        last = cp + 1;
                        continue;
                }
-               for ( ; cp < ep && *cp != sep; cp++)
-                       ;
+               while (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;
-
-                       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);
-
-                       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);
-                       }
-                       else
-                               memcpy(ent->base, last, 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);
-                               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;
+                       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);
                        }
                }
                while (cp < ep && *cp == sep)
@@ -311,23 +316,14 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
        }
 }
 
-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;
@@ -340,11 +336,26 @@ void prepare_alt_odb(void)
        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);
@@ -475,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);
                }
        }
@@ -583,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;
@@ -592,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();
@@ -606,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)
@@ -678,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;
@@ -830,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;
@@ -860,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);
@@ -880,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 {
@@ -896,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;
@@ -934,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) {
@@ -1073,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:
@@ -1111,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;
 }
 
@@ -1125,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(*((int*)(index + 24 * mi)));
+                       e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
                        memcpy(e->sha1, sha1, 20);
                        e->p = p;
                        return 1;
@@ -1178,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",
@@ -1222,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;
 }
 
@@ -1261,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;
                }
@@ -1365,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;
@@ -1431,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);
@@ -1440,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,
@@ -1545,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;
@@ -1611,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) {
@@ -1629,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);