#include "commit.h"
#include "tag.h"
#include "tree.h"
+#include "tree-walk.h"
#include "refs.h"
#include "pack-revindex.h"
#include "sha1-lookup.h"
static unsigned int pack_mmap_calls;
static unsigned int peak_pack_open_windows;
static unsigned int pack_open_windows;
+static unsigned int pack_open_fds;
+static unsigned int pack_max_fds;
static size_t peak_pack_mapped;
static size_t pack_mapped;
struct packed_git *packed_git;
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;
}
}
if (strcmp(pack_name, p->pack_name) == 0) {
clear_delta_base_cache();
close_pack_windows(p);
- if (p->pack_fd != -1)
+ if (p->pack_fd != -1) {
close(p->pack_fd);
+ pack_open_fds--;
+ }
close_pack_index(p);
free(p->bad_object_sha1);
*pp = p->next;
if (!p->index_data && open_pack_index(p))
return error("packfile %s index unavailable", p->pack_name);
+ if (!pack_max_fds) {
+ struct rlimit lim;
+ unsigned int max_fds;
+
+ if (getrlimit(RLIMIT_NOFILE, &lim))
+ die_errno("cannot get RLIMIT_NOFILE");
+
+ max_fds = lim.rlim_cur;
+
+ /* Save 3 for stdin/stdout/stderr, 22 for work */
+ if (25 < max_fds)
+ pack_max_fds = max_fds - 25;
+ else
+ pack_max_fds = 1;
+ }
+
+ while (pack_max_fds <= pack_open_fds && unuse_one_window(NULL, -1))
+ ; /* nothing */
+
p->pack_fd = git_open_noatime(p->pack_name, p);
if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
return -1;
+ pack_open_fds++;
/* If we created the struct before we had the pack we lack size. */
if (!p->pack_size) {
return 0;
if (p->pack_fd != -1) {
close(p->pack_fd);
+ pack_open_fds--;
p->pack_fd = -1;
}
return -1;
{
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?)");
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;
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)
void install_packed_git(struct packed_git *pack)
{
+ if (pack->pack_fd != -1)
+ pack_open_fds++;
+
pack->next = packed_git;
packed_git = pack;
}
sprintf(path, "%s/pack", objdir);
len = strlen(path);
dir = opendir(path);
- while (!dir && errno == EMFILE && unuse_one_window(NULL, -1))
- dir = opendir(path);
if (!dir) {
if (errno != ENOENT)
error("unable to open object pack directory: %s: %s",
if (fd >= 0)
return fd;
- /* Might the failure be insufficient file descriptors? */
- if (errno == EMFILE) {
- if (unuse_one_window(p, -1))
- continue;
- else
- return -1;
- }
-
/* Might the failure be due to O_NOATIME? */
if (errno != ENOENT && sha1_file_open_flag) {
sha1_file_open_flag = 0;
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;
* 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;
}
filename = sha1_file_name(sha1);
fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
- while (fd < 0 && errno == EMFILE && unuse_one_window(NULL, -1))
- fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
if (fd < 0) {
if (errno == EACCES)
return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
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;
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);
#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);
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);
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;