Пример #1
0
/* Hash everything but the last 20B of input */
static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
{
	size_t to_expell, to_keep;

	if (size == 0)
		return;

	/* Easy case, dump the buffer and the data minus the last 20 bytes */
	if (size >= GIT_OID_RAWSZ) {
		git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
		git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);

		data += size - GIT_OID_RAWSZ;
		memcpy(idx->inbuf, data, GIT_OID_RAWSZ);
		idx->inbuf_len = GIT_OID_RAWSZ;
		return;
	}

	/* We can just append */
	if (idx->inbuf_len + size <= GIT_OID_RAWSZ) {
		memcpy(idx->inbuf + idx->inbuf_len, data, size);
		idx->inbuf_len += size;
		return;
	}

	/* We need to partially drain the buffer and then append */
	to_keep   = GIT_OID_RAWSZ - size;
	to_expell = idx->inbuf_len - to_keep;

	git_hash_update(&idx->trailer, idx->inbuf, to_expell);

	memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
	memcpy(idx->inbuf + to_keep, data, size);
	idx->inbuf_len += size - to_expell;
}
Пример #2
0
static int write_deflate(git_filebuf *file, const void *source, size_t len)
{
    int result = Z_OK;
    z_stream *zs = &file->zs;

    if (len > 0 || file->flush_mode == Z_FINISH) {
        zs->next_in = (void *)source;
        zs->avail_in = len;

        do {
            int have;

            zs->next_out = file->z_buf;
            zs->avail_out = file->buf_size;

            result = deflate(zs, file->flush_mode);
            assert(result != Z_STREAM_ERROR);

            have = file->buf_size - zs->avail_out;

            if (gitfo_write(file->fd, file->z_buf, have) < GIT_SUCCESS)
                return git__throw(GIT_EOSERR, "Failed to write to file");

        } while (zs->avail_out == 0);

        assert(zs->avail_in == 0);

        if (file->digest)
            git_hash_update(file->digest, source, len);
    }

    return GIT_SUCCESS;
}
Пример #3
0
int git_filebuf_write(git_filebuf *file, void *buff, size_t len)
{
	int error;
	unsigned char *buf = buff;

	for (;;) {
		size_t space_left = file->buf_size - file->buf_pos;

		/* cache if it's small */
		if (space_left > len) {
			add_to_cache(file, buf, len);
			return GIT_SUCCESS;
		}

		/* flush the cache if it doesn't fit */
		if (file->buf_pos > 0) {
			add_to_cache(file, buf, space_left);

			if ((error = flush_buffer(file)) < GIT_SUCCESS)
				return error;

			len -= space_left;
			buf += space_left;
		}

		/* write too-large chunks immediately */
		if (len > file->buf_size) {
			error = gitfo_write(file->fd, buf, len);
			if (file->digest)
				git_hash_update(file->digest, buf, len);
		}
	}
}
Пример #4
0
static void hash_header(git_hash_ctx *ctx, size_t size, git_otype type)
{
	char header[64];
	int hdrlen;

	hdrlen = git_odb__format_object_header(header, sizeof(header), size, type);
	git_hash_update(ctx, header, hdrlen);
}
Пример #5
0
static void hash_header(git_hash_ctx *ctx, git_off_t len, git_otype type)
{
	char buffer[64];
	size_t hdrlen;

	hdrlen = git_odb__format_object_header(buffer, sizeof(buffer), (size_t)len, type);
	git_hash_update(ctx, buffer, hdrlen);
}
Пример #6
0
void test_odb_largefiles__streamread(void)
{
	git_oid oid, read_oid;
	git_odb_stream *stream;
	char buf[10240];
	char hdr[64];
	size_t len, hdr_len, total = 0;
	git_hash_ctx hash;
	git_otype type;
	int ret;

#ifndef GIT_ARCH_64
	cl_skip();
#endif

	if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
		!cl_is_env_set("GITTEST_SLOW"))
		cl_skip();

	writefile(&oid);

	cl_git_pass(git_odb_open_rstream(&stream, &len, &type, odb, &oid));

	cl_assert_equal_sz(LARGEFILE_SIZE, len);
	cl_assert_equal_i(GIT_OBJ_BLOB, type);

	cl_git_pass(git_hash_ctx_init(&hash));
	cl_git_pass(git_odb__format_object_header(&hdr_len, hdr, sizeof(hdr), len, type));

	cl_git_pass(git_hash_update(&hash, hdr, hdr_len));

	while ((ret = git_odb_stream_read(stream, buf, 10240)) > 0) {
		cl_git_pass(git_hash_update(&hash, buf, ret));
		total += ret;
	}

	cl_assert_equal_sz(LARGEFILE_SIZE, total);

	git_hash_final(&read_oid, &hash);

	cl_assert_equal_oid(&oid, &read_oid);

	git_hash_ctx_cleanup(&hash);
	git_odb_stream_free(stream);
}
Пример #7
0
int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
{
	int hdr_len;
	char hdr[64], buffer[2048];
	git_hash_ctx ctx;
	ssize_t read_len = 0;
	int error = 0;

	if (!git_object_typeisloose(type)) {
		giterr_set(GITERR_INVALID, "Invalid object type for hash");
		return -1;
	}

	if ((error = git_hash_ctx_init(&ctx)) < 0)
		return -1;

	hdr_len = git_odb__format_object_header(hdr, sizeof(hdr), size, type);

	if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0)
		goto done;

	while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
		if ((error = git_hash_update(&ctx, buffer, read_len)) < 0)
			goto done;

		size -= read_len;
	}

	/* If p_read returned an error code, the read obviously failed.
	 * If size is not zero, the file was truncated after we originally
	 * stat'd it, so we consider this a read failure too */
	if (read_len < 0 || size > 0) {
		giterr_set(GITERR_OS, "Error reading file for hashing");
		error = -1;

		goto done;
	}

	error = git_hash_final(out, &ctx);

done:
	git_hash_ctx_cleanup(&ctx);
	return error;
}
Пример #8
0
static int lock_file(git_filebuf *file, int flags)
{
	if (git_path_exists(file->path_lock) == true) {
		if (flags & GIT_FILEBUF_FORCE)
			p_unlink(file->path_lock);
		else {
			giterr_clear(); /* actual OS error code just confuses */
			giterr_set(GITERR_OS,
				"Failed to lock file '%s' for writing", file->path_lock);
			return -1;
		}
	}

	/* create path to the file buffer is required */
	if (flags & GIT_FILEBUF_FORCE) {
		/* XXX: Should dirmode here be configurable? Or is 0777 always fine? */
		file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, GIT_LOCK_FILE_MODE);
	} else {
		file->fd = git_futils_creat_locked(file->path_lock, GIT_LOCK_FILE_MODE);
	}

	if (file->fd < 0)
		return -1;

	file->fd_is_open = true;

	if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
		git_file source;
		char buffer[2048];
		ssize_t read_bytes;

		source = p_open(file->path_original, O_RDONLY);
		if (source < 0) {
			giterr_set(GITERR_OS,
				"Failed to open file '%s' for reading",
				file->path_original);
			return -1;
		}

		while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
			p_write(file->fd, buffer, read_bytes);
			if (file->digest)
				git_hash_update(file->digest, buffer, read_bytes);
		}

		p_close(source);

		if (read_bytes < 0) {
			giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
			return -1;
		}
	}

	return 0;
}
Пример #9
0
int git_hash_final(git_oid *out, git_hash_ctx *ctx)
{
	static const unsigned char pad[64] = { 0x80 };
	unsigned int padlen[2];
	int i;

	/* Pad with a binary 1 (ie 0x80), then zeroes, then length */
	padlen[0] = htonl((uint32_t)(ctx->size >> 29));
	padlen[1] = htonl((uint32_t)(ctx->size << 3));

	i = ctx->size & 63;
	git_hash_update(ctx, pad, 1+ (63 & (55 - i)));
	git_hash_update(ctx, padlen, 8);

	/* Output hash */
	for (i = 0; i < 5; i++)
		put_be32(out->id + i*4, ctx->H[i]);

	return 0;
}
Пример #10
0
int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len)
{
	git_hash_update((git_hash_ctx*)stream->hash_ctx, buffer, len);

	stream->received_bytes += len;

	if (stream->received_bytes > stream->declared_size)
		return git_odb_stream__invalid_length(stream,
			"stream_write()");

	return stream->write(stream, buffer, len);
}
Пример #11
0
static int write_normal(git_filebuf *file, const void *source, size_t len)
{
    int result = 0;

    if (len > 0) {
        result = gitfo_write(file->fd, (void *)source, len);
        if (file->digest)
            git_hash_update(file->digest, source, len);
    }

    return result;
}
Пример #12
0
void test_object_raw_hash__hash_by_blocks(void)
{
    git_hash_ctx *ctx;
    git_oid id1, id2;

    cl_assert((ctx = git_hash_new_ctx()) != NULL);

	/* should already be init'd */
    git_hash_update(ctx, hello_text, strlen(hello_text));
    git_hash_final(&id2, ctx);
    cl_git_pass(git_oid_fromstr(&id1, hello_id));
    cl_assert(git_oid_cmp(&id1, &id2) == 0);

	/* reinit should permit reuse */
    git_hash_init(ctx);
    git_hash_update(ctx, bye_text, strlen(bye_text));
    git_hash_final(&id2, ctx);
    cl_git_pass(git_oid_fromstr(&id1, bye_id));
    cl_assert(git_oid_cmp(&id1, &id2) == 0);

    git_hash_free_ctx(ctx);
}
Пример #13
0
void test_object_raw_hash__hash_by_blocks(void)
{
    git_hash_ctx ctx;
    git_oid id1, id2;

	cl_git_pass(git_hash_ctx_init(&ctx));

	/* should already be init'd */
    cl_git_pass(git_hash_update(&ctx, hello_text, strlen(hello_text)));
    cl_git_pass(git_hash_final(&id2, &ctx));
    cl_git_pass(git_oid_fromstr(&id1, hello_id));
    cl_assert(git_oid_cmp(&id1, &id2) == 0);

	/* reinit should permit reuse */
    cl_git_pass(git_hash_init(&ctx));
    cl_git_pass(git_hash_update(&ctx, bye_text, strlen(bye_text)));
    cl_git_pass(git_hash_final(&id2, &ctx));
    cl_git_pass(git_oid_fromstr(&id1, bye_id));
    cl_assert(git_oid_cmp(&id1, &id2) == 0);

    git_hash_ctx_cleanup(&ctx);
}
Пример #14
0
static int flush_buffer(git_filebuf *file)
{
	int result = GIT_SUCCESS;

	if (file->buf_pos > 0) {
		result = gitfo_write(file->fd, file->buffer, file->buf_pos);
		if (file->digest)
			git_hash_update(file->digest, file->buffer, file->buf_pos);

		file->buf_pos = 0;
	}

	return result;
}
Пример #15
0
static int write_normal(git_filebuf *file, void *source, size_t len)
{
	if (len > 0) {
		if (p_write(file->fd, (void *)source, len) < 0) {
			file->last_error = BUFERR_WRITE;
			return -1;
		}

		if (file->digest)
			git_hash_update(file->digest, source, len);
	}

	return 0;
}
Пример #16
0
int git_hash_buf(git_oid *out, const void *data, size_t len)
{
	git_hash_ctx ctx;
	int error = 0;

	if (git_hash_ctx_init(&ctx) < 0)
		return -1;

	if ((error = git_hash_update(&ctx, data, len)) >= 0)
		error = git_hash_final(out, &ctx);

	git_hash_ctx_cleanup(&ctx);
	
	return error;
}
Пример #17
0
static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
{
	ssize_t read;

	assert(idx && stream);

	do {
		if ((read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf))) < 0)
			break;

		git_hash_update(&idx->hash_ctx, idx->objbuf, read);
	} while (read > 0);

	if (read < 0)
		return (int)read;

	return 0;
}
Пример #18
0
int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)
{
	git_hash_ctx ctx;
	size_t i;
	int error = 0;

	if (git_hash_ctx_init(&ctx) < 0)
		return -1;

	for (i = 0; i < n; i++) {
		if ((error = git_hash_update(&ctx, vec[i].data, vec[i].len)) < 0)
			goto done;
	}

	error = git_hash_final(out, &ctx);

done:
	git_hash_ctx_cleanup(&ctx);

	return error;
}
Пример #19
0
static int lock_file(git_filebuf *file, int flags)
{
    if (gitfo_exists(file->path_lock) == 0) {
        if (flags & GIT_FILEBUF_FORCE)
            gitfo_unlink(file->path_lock);
        else
            return git__throw(GIT_EOSERR, "Failed to lock file");
    }

    /* create path to the file buffer is required */
    if (flags & GIT_FILEBUF_FORCE) {
        file->fd = gitfo_creat_locked_force(file->path_lock, 0644);
    } else {
        file->fd = gitfo_creat_locked(file->path_lock, 0644);
    }

    if (file->fd < 0)
        return git__throw(GIT_EOSERR, "Failed to create lock");

    if ((flags & GIT_FILEBUF_APPEND) && gitfo_exists(file->path_original) == 0) {
        git_file source;
        char buffer[2048];
        size_t read_bytes;

        source = gitfo_open(file->path_original, O_RDONLY);
        if (source < 0)
            return git__throw(GIT_EOSERR, "Failed to lock file. Could not open %s", file->path_original);

        while ((read_bytes = gitfo_read(source, buffer, 2048)) > 0) {
            gitfo_write(file->fd, buffer, read_bytes);
            if (file->digest)
                git_hash_update(file->digest, buffer, read_bytes);
        }

        gitfo_close(source);
    }

    return GIT_SUCCESS;
}
Пример #20
0
static int write_deflate(git_filebuf *file, void *source, size_t len)
{
	z_stream *zs = &file->zs;

	if (len > 0 || file->flush_mode == Z_FINISH) {
		zs->next_in = source;
		zs->avail_in = (uInt)len;

		do {
			size_t have;

			zs->next_out = file->z_buf;
			zs->avail_out = (uInt)file->buf_size;

			if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {
				file->last_error = BUFERR_ZLIB;
				return -1;
			}

			have = file->buf_size - (size_t)zs->avail_out;

			if (p_write(file->fd, file->z_buf, have) < 0) {
				file->last_error = BUFERR_WRITE;
				return -1;
			}

		} while (zs->avail_out == 0);

		assert(zs->avail_in == 0);

		if (file->digest)
			git_hash_update(file->digest, source, len);
	}

	return 0;
}
Пример #21
0
static int lock_file(git_filebuf *file, int flags)
{
	if (gitfo_exists(file->path_lock) == 0) {
		if (flags & GIT_FILEBUF_FORCE)
			gitfo_unlink(file->path_lock);
		else
			return GIT_EOSERR;
	}

	file->fd = gitfo_creat(file->path_lock, 0644);

	if (file->fd < 0)
		return GIT_EOSERR;

	/* TODO: do a flock() in the descriptor file_lock */

	if ((flags & GIT_FILEBUF_APPEND) && gitfo_exists(file->path_original) == 0) {
		git_file source;
		char buffer[2048];
		size_t read_bytes;

		source = gitfo_open(file->path_original, O_RDONLY);
		if (source < 0)
			return GIT_EOSERR;

		while ((read_bytes = gitfo_read(source, buffer, 2048)) > 0) {
			gitfo_write(file->fd, buffer, read_bytes);
			if (file->digest)
				git_hash_update(file->digest, buffer, read_bytes);
		}

		gitfo_close(source);
	}

	return GIT_SUCCESS;
}
Пример #22
0
int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
{
	git_mwindow *w = NULL;
	unsigned int i, long_offsets = 0, left;
	int error;
	struct git_pack_idx_header hdr;
	git_buf filename = GIT_BUF_INIT;
	struct entry *entry;
	git_oid trailer_hash, file_hash;
	git_hash_ctx ctx;
	git_filebuf index_file = {0};
	void *packfile_trailer;

	if (git_hash_ctx_init(&ctx) < 0)
		return -1;

	/* Test for this before resolve_deltas(), as it plays with idx->off */
	if (idx->off < idx->pack->mwf.size - 20) {
		giterr_set(GITERR_INDEXER, "Unexpected data at the end of the pack");
		return -1;
	}

	packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
	if (packfile_trailer == NULL) {
		git_mwindow_close(&w);
		goto on_error;
	}

	/* Compare the packfile trailer as it was sent to us and what we calculated */
	git_oid_fromraw(&file_hash, (unsigned char*) packfile_trailer);
	git_mwindow_close(&w);

	git_hash_final(&trailer_hash, &idx->trailer);
	if (git_oid_cmp(&file_hash, &trailer_hash)) {
		giterr_set(GITERR_INDEXER, "packfile trailer mismatch");
		return -1;
	}

	/* Freeze the number of deltas */
	stats->total_deltas = stats->total_objects - stats->indexed_objects;

	if ((error = resolve_deltas(idx, stats)) < 0)
		return error;

	if (stats->indexed_objects != stats->total_objects) {
		giterr_set(GITERR_INDEXER, "early EOF");
		return -1;
	}

	if (stats->local_objects > 0) {
		if (update_header_and_rehash(idx, stats) < 0)
			return -1;

		git_hash_final(&trailer_hash, &idx->trailer);
		write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ);
	}

	git_vector_sort(&idx->objects);

	git_buf_sets(&filename, idx->pack->pack_name);
	git_buf_shorten(&filename, strlen("pack"));
	git_buf_puts(&filename, "idx");
	if (git_buf_oom(&filename))
		return -1;

	if (git_filebuf_open(&index_file, filename.ptr,
		GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0)
		goto on_error;

	/* Write out the header */
	hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
	hdr.idx_version = htonl(2);
	git_filebuf_write(&index_file, &hdr, sizeof(hdr));

	/* Write out the fanout table */
	for (i = 0; i < 256; ++i) {
		uint32_t n = htonl(idx->fanout[i]);
		git_filebuf_write(&index_file, &n, sizeof(n));
	}

	/* Write out the object names (SHA-1 hashes) */
	git_vector_foreach(&idx->objects, i, entry, struct entry*) {
		git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid));
		git_hash_update(&ctx, &entry->oid, GIT_OID_RAWSZ);
	}
	git_hash_final(&idx->hash, &ctx);

	/* Write out the CRC32 values */
	git_vector_foreach(&idx->objects, i, entry, struct entry*) {
		git_filebuf_write(&index_file, &entry->crc, sizeof(uint32_t));
	}

	/* Write out the offsets */
	git_vector_foreach(&idx->objects, i, entry, struct entry*) {
		uint32_t n;

		if (entry->offset == UINT32_MAX)
			n = htonl(0x80000000 | long_offsets++);
		else
			n = htonl(entry->offset);

		git_filebuf_write(&index_file, &n, sizeof(uint32_t));
	}

	/* Write out the long offsets */
	git_vector_foreach(&idx->objects, i, entry, struct entry*) {
		uint32_t split[2];

		if (entry->offset != UINT32_MAX)
			continue;

		split[0] = htonl(entry->offset_long >> 32);
		split[1] = htonl(entry->offset_long & 0xffffffff);

		git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
	}

	/* Write out the packfile trailer to the index */
	if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0)
		goto on_error;

	/* Write out the hash of the idx */
	if (git_filebuf_hash(&trailer_hash, &index_file) < 0)
		goto on_error;

	git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid));

	/* Figure out what the final name should be */
	if (index_path(&filename, idx, ".idx") < 0)
		goto on_error;

	/* Commit file */
	if (git_filebuf_commit_at(&index_file, filename.ptr) < 0)
		goto on_error;

	git_mwindow_free_all(&idx->pack->mwf);
	/* We need to close the descriptor here so Windows doesn't choke on commit_at */
	if (p_close(idx->pack->mwf.fd) < 0) {
		giterr_set(GITERR_OS, "failed to close packfile");
		goto on_error;
	}

	idx->pack->mwf.fd = -1;

	if (index_path(&filename, idx, ".pack") < 0)
		goto on_error;

	/* And don't forget to rename the packfile to its new place. */
	p_rename(idx->pack->pack_name, git_buf_cstr(&filename));

	git_buf_free(&filename);
	git_hash_ctx_cleanup(&ctx);
	return 0;

on_error:
	git_mwindow_free_all(&idx->pack->mwf);
	git_filebuf_cleanup(&index_file);
	git_buf_free(&filename);
	git_hash_ctx_cleanup(&ctx);
	return -1;
}