Blame src/blob.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
Packit Service 20376f
#include "git2/common.h"
Packit Service 20376f
#include "git2/object.h"
Packit Service 20376f
#include "git2/repository.h"
Packit Service 20376f
#include "git2/odb_backend.h"
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include "filebuf.h"
Packit Service 20376f
#include "blob.h"
Packit Service 20376f
#include "filter.h"
Packit Service 20376f
#include "buf_text.h"
Packit Service 20376f
Packit Service 20376f
const void *git_blob_rawcontent(const git_blob *blob)
Packit Service 20376f
{
Packit Service 20376f
	assert(blob);
Packit Service 20376f
	return git_odb_object_data(blob->odb_object);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
git_off_t git_blob_rawsize(const git_blob *blob)
Packit Service 20376f
{
Packit Service 20376f
	assert(blob);
Packit Service 20376f
	return (git_off_t)git_odb_object_size(blob->odb_object);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob__getbuf(git_buf *buffer, git_blob *blob)
Packit Service 20376f
{
Packit Service 20376f
	return git_buf_set(
Packit Service 20376f
		buffer,
Packit Service 20376f
		git_odb_object_data(blob->odb_object),
Packit Service 20376f
		git_odb_object_size(blob->odb_object));
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void git_blob__free(void *blob)
Packit Service 20376f
{
Packit Service 20376f
	git_odb_object_free(((git_blob *)blob)->odb_object);
Packit Service 20376f
	git__free(blob);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob__parse(void *blob, git_odb_object *odb_obj)
Packit Service 20376f
{
Packit Service 20376f
	assert(blob);
Packit Service 20376f
	git_cached_obj_incref((git_cached_obj *)odb_obj);
Packit Service 20376f
	((git_blob *)blob)->odb_object = odb_obj;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob_create_frombuffer(
Packit Service 20376f
	git_oid *id, git_repository *repo, const void *buffer, size_t len)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_odb *odb;
Packit Service 20376f
	git_odb_stream *stream;
Packit Service 20376f
Packit Service 20376f
	assert(id && repo);
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
Packit Service 20376f
		(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
Packit Service 20376f
		error = git_odb_stream_finalize_write(id, stream);
Packit Service 20376f
Packit Service 20376f
	git_odb_stream_free(stream);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_file_stream(
Packit Service 20376f
	git_oid *id, git_odb *odb, const char *path, git_off_t file_size)
Packit Service 20376f
{
Packit Service 20376f
	int fd, error;
Packit Service 20376f
	char buffer[FILEIO_BUFSIZE];
Packit Service 20376f
	git_odb_stream *stream = NULL;
Packit Service 20376f
	ssize_t read_len = -1;
Packit Service 20376f
	git_off_t written = 0;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_odb_open_wstream(
Packit Service 20376f
			&stream, odb, file_size, GIT_OBJ_BLOB)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((fd = git_futils_open_ro(path)) < 0) {
Packit Service 20376f
		git_odb_stream_free(stream);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
Packit Service 20376f
		error = git_odb_stream_write(stream, buffer, read_len);
Packit Service 20376f
		written += read_len;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	p_close(fd);
Packit Service 20376f
Packit Service 20376f
	if (written != file_size || read_len < 0) {
Packit Service 20376f
		giterr_set(GITERR_OS, "failed to read file into stream");
Packit Service 20376f
		error = -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!error)
Packit Service 20376f
		error = git_odb_stream_finalize_write(id, stream);
Packit Service 20376f
Packit Service 20376f
	git_odb_stream_free(stream);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_file_filtered(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	git_off_t *size,
Packit Service 20376f
	git_odb *odb,
Packit Service 20376f
	const char *full_path,
Packit Service 20376f
	git_filter_list *fl)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_buf tgt = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
Packit Service 20376f
Packit Service 20376f
	/* Write the file to disk if it was properly filtered */
Packit Service 20376f
	if (!error) {
Packit Service 20376f
		*size = tgt.size;
Packit Service 20376f
Packit Service 20376f
		error = git_odb_write(id, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&tgt);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int write_symlink(
Packit Service 20376f
	git_oid *id, git_odb *odb, const char *path, size_t link_size)
Packit Service 20376f
{
Packit Service 20376f
	char *link_data;
Packit Service 20376f
	ssize_t read_len;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	link_data = git__malloc(link_size);
Packit Service 20376f
	GITERR_CHECK_ALLOC(link_data);
Packit Service 20376f
Packit Service 20376f
	read_len = p_readlink(path, link_data, link_size);
Packit Service 20376f
	if (read_len != (ssize_t)link_size) {
Packit Service 20376f
		giterr_set(GITERR_OS, "failed to create blob: cannot read symlink '%s'", path);
Packit Service 20376f
		git__free(link_data);
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	error = git_odb_write(id, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
Packit Service 20376f
	git__free(link_data);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob__create_from_paths(
Packit Service 20376f
	git_oid *id,
Packit Service 20376f
	struct stat *out_st,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *content_path,
Packit Service 20376f
	const char *hint_path,
Packit Service 20376f
	mode_t hint_mode,
Packit Service 20376f
	bool try_load_filters)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	struct stat st;
Packit Service 20376f
	git_odb *odb = NULL;
Packit Service 20376f
	git_off_t size;
Packit Service 20376f
	mode_t mode;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	assert(hint_path || !try_load_filters);
Packit Service 20376f
Packit Service 20376f
	if (!content_path) {
Packit Service 20376f
		if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
Packit Service 20376f
			return GIT_EBAREREPO;
Packit Service 20376f
Packit Service 20376f
		if (git_buf_joinpath(
Packit Service 20376f
				&path, git_repository_workdir(repo), hint_path) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		content_path = path.ptr;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_path_lstat(content_path, &st)) < 0 ||
Packit Service 20376f
		(error = git_repository_odb(&odb, repo)) < 0)
Packit Service 20376f
		goto done;
Packit Service 20376f
Packit Service 20376f
	if (S_ISDIR(st.st_mode)) {
Packit Service 20376f
		giterr_set(GITERR_ODB, "cannot create blob from '%s': it is a directory", content_path);
Packit Service 20376f
		error = GIT_EDIRECTORY;
Packit Service 20376f
		goto done;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (out_st)
Packit Service 20376f
		memcpy(out_st, &st, sizeof(st));
Packit Service 20376f
Packit Service 20376f
	size = st.st_size;
Packit Service 20376f
	mode = hint_mode ? hint_mode : st.st_mode;
Packit Service 20376f
Packit Service 20376f
	if (S_ISLNK(mode)) {
Packit Service 20376f
		error = write_symlink(id, odb, content_path, (size_t)size);
Packit Service 20376f
	} else {
Packit Service 20376f
		git_filter_list *fl = NULL;
Packit Service 20376f
Packit Service 20376f
		if (try_load_filters)
Packit Service 20376f
			/* Load the filters for writing this file to the ODB */
Packit Service 20376f
			error = git_filter_list_load(
Packit Service 20376f
				&fl, repo, NULL, hint_path,
Packit Service 20376f
				GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
Packit Service 20376f
Packit Service 20376f
		if (error < 0)
Packit Service 20376f
			/* well, that didn't work */;
Packit Service 20376f
		else if (fl == NULL)
Packit Service 20376f
			/* No filters need to be applied to the document: we can stream
Packit Service 20376f
			 * directly from disk */
Packit Service 20376f
			error = write_file_stream(id, odb, content_path, size);
Packit Service 20376f
		else {
Packit Service 20376f
			/* We need to apply one or more filters */
Packit Service 20376f
			error = write_file_filtered(id, &size, odb, content_path, fl);
Packit Service 20376f
Packit Service 20376f
			git_filter_list_free(fl);
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/*
Packit Service 20376f
		 * TODO: eventually support streaming filtered files, for files
Packit Service 20376f
		 * which are bigger than a given threshold. This is not a priority
Packit Service 20376f
		 * because applying a filter in streaming mode changes the final
Packit Service 20376f
		 * size of the blob, and without knowing its final size, the blob
Packit Service 20376f
		 * cannot be written in stream mode to the ODB.
Packit Service 20376f
		 *
Packit Service 20376f
		 * The plan is to do streaming writes to a tempfile on disk and then
Packit Service 20376f
		 * opening streaming that file to the ODB, using
Packit Service 20376f
		 * `write_file_stream`.
Packit Service 20376f
		 *
Packit Service 20376f
		 * CAREFULLY DESIGNED APIS YO
Packit Service 20376f
		 */
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
done:
Packit Service 20376f
	git_odb_free(odb);
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob_create_fromworkdir(
Packit Service 20376f
	git_oid *id, git_repository *repo, const char *path)
Packit Service 20376f
{
Packit Service 20376f
	return git_blob__create_from_paths(id, NULL, repo, NULL, path, 0, true);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob_create_fromdisk(
Packit Service 20376f
	git_oid *id, git_repository *repo, const char *path)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_buf full_path = GIT_BUF_INIT;
Packit Service 20376f
	const char *workdir, *hintpath;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
Packit Service 20376f
		git_buf_free(&full_path);
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	hintpath = git_buf_cstr(&full_path);
Packit Service 20376f
	workdir  = git_repository_workdir(repo);
Packit Service 20376f
Packit Service 20376f
	if (workdir && !git__prefixcmp(hintpath, workdir))
Packit Service 20376f
		hintpath += strlen(workdir);
Packit Service 20376f
Packit Service 20376f
	error = git_blob__create_from_paths(
Packit Service 20376f
		id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&full_path);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
typedef struct {
Packit Service 20376f
	git_writestream parent;
Packit Service 20376f
	git_filebuf fbuf;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	char *hintpath;
Packit Service 20376f
} blob_writestream;
Packit Service 20376f
Packit Service 20376f
static int blob_writestream_close(git_writestream *_stream)
Packit Service 20376f
{
Packit Service 20376f
	blob_writestream *stream = (blob_writestream *) _stream;
Packit Service 20376f
Packit Service 20376f
	git_filebuf_cleanup(&stream->fbuf);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static void blob_writestream_free(git_writestream *_stream)
Packit Service 20376f
{
Packit Service 20376f
	blob_writestream *stream = (blob_writestream *) _stream;
Packit Service 20376f
Packit Service 20376f
	git_filebuf_cleanup(&stream->fbuf);
Packit Service 20376f
	git__free(stream->hintpath);
Packit Service 20376f
	git__free(stream);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int blob_writestream_write(git_writestream *_stream, const char *buffer, size_t len)
Packit Service 20376f
{
Packit Service 20376f
	blob_writestream *stream = (blob_writestream *) _stream;
Packit Service 20376f
Packit Service 20376f
	return git_filebuf_write(&stream->fbuf, buffer, len);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob_create_fromstream(git_writestream **out, git_repository *repo, const char *hintpath)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_buf path = GIT_BUF_INIT;
Packit Service 20376f
	blob_writestream *stream;
Packit Service 20376f
Packit Service 20376f
	assert(out && repo);
Packit Service 20376f
Packit Service 20376f
	stream = git__calloc(1, sizeof(blob_writestream));
Packit Service 20376f
	GITERR_CHECK_ALLOC(stream);
Packit Service 20376f
Packit Service 20376f
	if (hintpath) {
Packit Service 20376f
		stream->hintpath = git__strdup(hintpath);
Packit Service 20376f
		GITERR_CHECK_ALLOC(stream->hintpath);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	stream->repo = repo;
Packit Service 20376f
	stream->parent.write = blob_writestream_write;
Packit Service 20376f
	stream->parent.close = blob_writestream_close;
Packit Service 20376f
	stream->parent.free  = blob_writestream_free;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_item_path(&path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
Packit Service 20376f
		|| (error = git_buf_joinpath(&path, path.ptr, "streamed")) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY,
Packit Service 20376f
					       0666, 2 * 1024 * 1024)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	*out = (git_writestream *) stream;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		blob_writestream_free((git_writestream *) stream);
Packit Service 20376f
Packit Service 20376f
	git_buf_free(&path);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob_create_fromstream_commit(git_oid *out, git_writestream *_stream)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	blob_writestream *stream = (blob_writestream *) _stream;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * We can make this more officient by avoiding writing to
Packit Service 20376f
	 * disk, but for now let's re-use the helper functions we
Packit Service 20376f
	 * have.
Packit Service 20376f
	 */
Packit Service 20376f
	if ((error = git_filebuf_flush(&stream->fbuf)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	error = git_blob__create_from_paths(out, NULL, stream->repo, stream->fbuf.path_lock,
Packit Service 20376f
					    stream->hintpath, 0, !!stream->hintpath);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	blob_writestream_free(_stream);
Packit Service 20376f
	return error;
Packit Service 20376f
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob_is_binary(const git_blob *blob)
Packit Service 20376f
{
Packit Service 20376f
	git_buf content = GIT_BUF_INIT;
Packit Service 20376f
Packit Service 20376f
	assert(blob);
Packit Service 20376f
Packit Service 20376f
	git_buf_attach_notowned(&content, blob->odb_object->buffer,
Packit Service 20376f
		min(blob->odb_object->cached.size,
Packit Service 20376f
		GIT_FILTER_BYTES_TO_CHECK_NUL));
Packit Service 20376f
	return git_buf_text_is_binary(&content);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_blob_filtered_content(
Packit Service 20376f
	git_buf *out,
Packit Service 20376f
	git_blob *blob,
Packit Service 20376f
	const char *path,
Packit Service 20376f
	int check_for_binary_data)
Packit Service 20376f
{
Packit Service 20376f
	int error = 0;
Packit Service 20376f
	git_filter_list *fl = NULL;
Packit Service 20376f
Packit Service 20376f
	assert(blob && path && out);
Packit Service 20376f
Packit Service 20376f
	git_buf_sanitize(out);
Packit Service 20376f
Packit Service 20376f
	if (check_for_binary_data && git_blob_is_binary(blob))
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	if (!(error = git_filter_list_load(
Packit Service 20376f
			&fl, git_blob_owner(blob), blob, path,
Packit Service 20376f
			GIT_FILTER_TO_WORKTREE, GIT_FILTER_DEFAULT))) {
Packit Service 20376f
Packit Service 20376f
		error = git_filter_list_apply_to_blob(out, fl, blob);
Packit Service 20376f
Packit Service 20376f
		git_filter_list_free(fl);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}