/* * Copyright (C) 2009-2012 the libgit2 contributors * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include "git2/common.h" #include "git2/object.h" #include "git2/repository.h" #include "common.h" #include "blob.h" #include "filter.h" const void *git_blob_rawcontent(git_blob *blob) { assert(blob); return blob->odb_object->raw.data; } size_t git_blob_rawsize(git_blob *blob) { assert(blob); return blob->odb_object->raw.len; } void git_blob__free(git_blob *blob) { git_odb_object_free(blob->odb_object); git__free(blob); } int git_blob__parse(git_blob *blob, git_odb_object *odb_obj) { assert(blob); git_cached_obj_incref((git_cached_obj *)odb_obj); blob->odb_object = odb_obj; return GIT_SUCCESS; } int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len) { int error; git_odb *odb; git_odb_stream *stream; error = git_repository_odb__weakptr(&odb, repo); if (error < GIT_SUCCESS) return error; if ((error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < GIT_SUCCESS) return git__rethrow(error, "Failed to create blob"); if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) { stream->free(stream); return error; } error = stream->finalize_write(oid, stream); stream->free(stream); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to create blob"); return GIT_SUCCESS; } static int write_file_stream(git_oid *oid, git_odb *odb, const char *path, git_off_t file_size) { int fd, error; char buffer[4096]; git_odb_stream *stream = NULL; if ((error = git_odb_open_wstream(&stream, odb, file_size, GIT_OBJ_BLOB)) < GIT_SUCCESS) return error; if ((fd = p_open(path, O_RDONLY)) < 0) { error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", path); goto cleanup; } while (file_size > 0) { ssize_t read_len = p_read(fd, buffer, sizeof(buffer)); if (read_len < 0) { error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file"); p_close(fd); goto cleanup; } stream->write(stream, buffer, read_len); file_size -= read_len; } p_close(fd); error = stream->finalize_write(oid, stream); cleanup: stream->free(stream); return error; } static int write_file_filtered( git_oid *oid, git_odb *odb, const char *path, git_vector *filters) { int error; git_buf file_in = GIT_BUF_INIT; git_buf filter_result = GIT_BUF_INIT; error = git_futils_readbuffer(&file_in, path); if (error < GIT_SUCCESS) return error; error = git_filter__apply(&filter_result, &file_in, filters, path); if (error < GIT_SUCCESS) { git_buf_free(&file_in); git_buf_free(&filter_result); return error; } error = git_odb_write(oid, odb, filter_result.ptr, filter_result.size, GIT_OBJ_BLOB); git_buf_free(&file_in); git_buf_free(&filter_result); return GIT_SUCCESS; } static int write_symlink(git_oid *oid, git_odb *odb, const char *path, size_t link_size) { char *link_data; ssize_t read_len; int error; link_data = git__malloc(link_size); if (!link_data) return GIT_ENOMEM; read_len = p_readlink(path, link_data, link_size); if (read_len != (ssize_t)link_size) { free(link_data); return git__throw(GIT_EOSERR, "Failed to create blob. Can't read symlink"); } error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB); free(link_data); return error; } int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path) { int error = GIT_SUCCESS; git_buf full_path = GIT_BUF_INIT; git_off_t size; struct stat st; const char *workdir; git_odb *odb = NULL; workdir = git_repository_workdir(repo); if (workdir == NULL) return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)"); error = git_buf_joinpath(&full_path, workdir, path); if (error < GIT_SUCCESS) return error; error = p_lstat(full_path.ptr, &st); if (error < 0) { error = git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno)); goto cleanup; } size = st.st_size; error = git_repository_odb__weakptr(&odb, repo); if (error < GIT_SUCCESS) goto cleanup; if (S_ISLNK(st.st_mode)) { error = write_symlink(oid, odb, full_path.ptr, (size_t)size); } else { git_vector write_filters = GIT_VECTOR_INIT; if ((error = git_filter__load_for_file( &write_filters, repo, full_path.ptr, GIT_FILTER_TO_ODB)) < GIT_SUCCESS) goto cleanup; if (write_filters.length == 0) { error = write_file_stream(oid, odb, full_path.ptr, size); } else { error = write_file_filtered(oid, odb, full_path.ptr, &write_filters); } /* * TODO: eventually support streaming filtered files, for files which are bigger * than a given threshold. This is not a priority because applying a filter in * streaming mode changes the final size of the blob, and without knowing its * final size, the blob cannot be written in stream mode to the ODB. * * The plan is to do streaming writes to a tempfile on disk and then opening * streaming that file to the ODB, using `write_file_stream`. * * CAREFULLY DESIGNED APIS YO */ } cleanup: git_buf_free(&full_path); return error; }