i18n: do not overuse C_LOCALE_OUTPUT (grep)
[gitweb.git] / sha1_file.c
index c6beed2d6b727a72c94a14e5fc63b217946c75f8..df0edbad1e810727e384a5753001dc8c85d4ecf9 100644 (file)
@@ -13,6 +13,7 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
+#include "tree-walk.h"
 #include "refs.h"
 #include "pack-revindex.h"
 #include "sha1-lookup.h"
 #endif
 #endif
 
-#ifdef NO_C99_FORMAT
-#define SZ_FMT "lu"
-static unsigned long sz_fmt(size_t s) { return (unsigned long)s; }
-#else
-#define SZ_FMT "zu"
-static size_t sz_fmt(size_t s) { return s; }
-#endif
+#define SZ_FMT PRIuMAX
+static inline uintmax_t sz_fmt(size_t s) { return s; }
 
 const unsigned char null_sha1[20];
 
@@ -72,6 +68,35 @@ static struct cached_object *find_cached_object(const unsigned char *sha1)
        return NULL;
 }
 
+int mkdir_in_gitdir(const char *path)
+{
+       if (mkdir(path, 0777)) {
+               int saved_errno = errno;
+               struct stat st;
+               struct strbuf sb = STRBUF_INIT;
+
+               if (errno != EEXIST)
+                       return -1;
+               /*
+                * Are we looking at a path in a symlinked worktree
+                * whose original repository does not yet have it?
+                * e.g. .git/rr-cache pointing at its original
+                * repository in which the user hasn't performed any
+                * conflict resolution yet?
+                */
+               if (lstat(path, &st) || !S_ISLNK(st.st_mode) ||
+                   strbuf_readlink(&sb, path, st.st_size) ||
+                   !is_absolute_path(sb.buf) ||
+                   mkdir(sb.buf, 0777)) {
+                       strbuf_release(&sb);
+                       errno = saved_errno;
+                       return -1;
+               }
+               strbuf_release(&sb);
+       }
+       return adjust_shared_perm(path);
+}
+
 int safe_create_leading_directories(char *path)
 {
        char *pos = path + offset_1st_component(path);
@@ -596,7 +621,8 @@ static int unuse_one_window(struct packed_git *current, int keep_fd)
                        lru_l->next = lru_w->next;
                else {
                        lru_p->windows = lru_w->next;
-                       if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
+                       if (!lru_p->windows && lru_p->pack_fd != -1
+                               && lru_p->pack_fd != keep_fd) {
                                close(lru_p->pack_fd);
                                pack_open_fds--;
                                lru_p->pack_fd = -1;
@@ -812,14 +838,13 @@ unsigned char *use_pack(struct packed_git *p,
 {
        struct pack_window *win = *w_cursor;
 
-       if (p->pack_fd == -1 && open_packed_git(p))
-               die("packfile %s cannot be accessed", p->pack_name);
-
        /* Since packfiles end in a hash of their content and it's
         * pointless to ask for an offset into the middle of that
         * hash, and the in_window function above wouldn't match
         * don't allow an offset too close to the end of the file.
         */
+       if (!p->pack_size && p->pack_fd == -1 && open_packed_git(p))
+               die("packfile %s cannot be accessed", p->pack_name);
        if (offset > (p->pack_size - 20))
                die("offset beyond end of packfile (truncated pack?)");
 
@@ -833,6 +858,10 @@ unsigned char *use_pack(struct packed_git *p,
                if (!win) {
                        size_t window_align = packed_git_window_size / 2;
                        off_t len;
+
+                       if (p->pack_fd == -1 && open_packed_git(p))
+                               die("packfile %s cannot be accessed", p->pack_name);
+
                        win = xcalloc(1, sizeof(*win));
                        win->offset = (offset / window_align) * window_align;
                        len = p->pack_size - win->offset;
@@ -850,6 +879,12 @@ unsigned char *use_pack(struct packed_git *p,
                                die("packfile %s cannot be mapped: %s",
                                        p->pack_name,
                                        strerror(errno));
+                       if (!win->offset && win->len == p->pack_size
+                               && !p->do_not_close) {
+                               close(p->pack_fd);
+                               pack_open_fds--;
+                               p->pack_fd = -1;
+                       }
                        pack_mmap_calls++;
                        pack_open_windows++;
                        if (pack_mapped > peak_pack_mapped)
@@ -1950,6 +1985,27 @@ off_t find_pack_entry_one(const unsigned char *sha1,
        return 0;
 }
 
+static int is_pack_valid(struct packed_git *p)
+{
+       /* An already open pack is known to be valid. */
+       if (p->pack_fd != -1)
+               return 1;
+
+       /* If the pack has one window completely covering the
+        * file size, the pack is known to be valid even if
+        * the descriptor is not currently open.
+        */
+       if (p->windows) {
+               struct pack_window *w = p->windows;
+
+               if (!w->offset && w->len == p->pack_size)
+                       return 1;
+       }
+
+       /* Force the pack to open to prove its valid. */
+       return !open_packed_git(p);
+}
+
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
        static struct packed_git *last_found = (void *)1;
@@ -1979,7 +2035,7 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
                         * it may have been deleted since the index
                         * was loaded!
                         */
-                       if (p->pack_fd == -1 && open_packed_git(p)) {
+                       if (!is_pack_valid(p)) {
                                error("packfile %s cannot be accessed", p->pack_name);
                                goto next;
                        }
@@ -2496,8 +2552,37 @@ int has_sha1_file(const unsigned char *sha1)
        return has_loose_object(sha1);
 }
 
+static void check_tree(const void *buf, size_t size)
+{
+       struct tree_desc desc;
+       struct name_entry entry;
+
+       init_tree_desc(&desc, buf, size);
+       while (tree_entry(&desc, &entry))
+               /* do nothing
+                * tree_entry() will die() on malformed entries */
+               ;
+}
+
+static void check_commit(const void *buf, size_t size)
+{
+       struct commit c;
+       memset(&c, 0, sizeof(c));
+       if (parse_commit_buffer(&c, buf, size))
+               die("corrupt commit");
+}
+
+static void check_tag(const void *buf, size_t size)
+{
+       struct tag t;
+       memset(&t, 0, sizeof(t));
+       if (parse_tag_buffer(&t, buf, size))
+               die("corrupt tag");
+}
+
 static int index_mem(unsigned char *sha1, void *buf, size_t size,
-                    int write_object, enum object_type type, const char *path)
+                    int write_object, enum object_type type,
+                    const char *path, int format_check)
 {
        int ret, re_allocated = 0;
 
@@ -2515,6 +2600,14 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
                        re_allocated = 1;
                }
        }
+       if (format_check) {
+               if (type == OBJ_TREE)
+                       check_tree(buf, size);
+               if (type == OBJ_COMMIT)
+                       check_commit(buf, size);
+               if (type == OBJ_TAG)
+                       check_tag(buf, size);
+       }
 
        if (write_object)
                ret = write_sha1_file(buf, size, typename(type), sha1);
@@ -2528,7 +2621,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
 #define SMALL_FILE_SIZE (32*1024)
 
 int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
-            enum object_type type, const char *path)
+            enum object_type type, const char *path, int format_check)
 {
        int ret;
        size_t size = xsize_t(st->st_size);
@@ -2537,23 +2630,25 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
                struct strbuf sbuf = STRBUF_INIT;
                if (strbuf_read(&sbuf, fd, 4096) >= 0)
                        ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
-                                       type, path);
+                                       type, path, format_check);
                else
                        ret = -1;
                strbuf_release(&sbuf);
        } else if (!size) {
-               ret = index_mem(sha1, NULL, size, write_object, type, path);
+               ret = index_mem(sha1, NULL, size, write_object, type, path,
+                               format_check);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
                        ret = index_mem(sha1, buf, size, write_object, type,
-                                       path);
+                                       path, format_check);
                else
                        ret = error("short read %s", strerror(errno));
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-               ret = index_mem(sha1, buf, size, write_object, type, path);
+               ret = index_mem(sha1, buf, size, write_object, type, path,
+                               format_check);
                munmap(buf, size);
        }
        close(fd);
@@ -2571,7 +2666,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
                if (fd < 0)
                        return error("open(\"%s\"): %s", path,
                                     strerror(errno));
-               if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0)
+               if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path, 0) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;