]> git.proxmox.com Git - libgit2.git/blobdiff - src/odb.c
Fix error hashing empty file.
[libgit2.git] / src / odb.c
index a6a18f83134e188733066bb7953ab238b7838814..d951bc51b49f12d3269e5f3e2cda0db6adf3a1ba 100644 (file)
--- a/src/odb.c
+++ b/src/odb.c
 #include "hash.h"
 #include "odb.h"
 #include "delta-apply.h"
+#include "filter.h"
 
 #include "git2/odb_backend.h"
+#include "git2/oid.h"
 
 #define GIT_ALTERNATES_FILE "info/alternates"
 
@@ -113,32 +115,67 @@ 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;
+
+       if (!git_object_typeisloose(type)) {
+               giterr_set(GITERR_INVALID, "Invalid object type for hash");
+               return -1;
+       }
 
        hdr_len = format_object_header(hdr, sizeof(hdr), size, type);
 
        ctx = git_hash_new_ctx();
+       GITERR_CHECK_ALLOC(ctx);
 
        git_hash_update(ctx, hdr, hdr_len);
 
-       while (size > 0) {
-               ssize_t read_len = read(fd, buffer, sizeof(buffer));
-
-               if (read_len < 0) {
-                       git_hash_free_ctx(ctx);
-                       giterr_set(GITERR_OS, "Error reading file");
-                       return -1;
-               }
-
+       while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
                git_hash_update(ctx, buffer, read_len);
                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) {
+               git_hash_free_ctx(ctx);
+               giterr_set(GITERR_OS, "Error reading file for hashing");
+               return -1;
+       }
+
        git_hash_final(out, ctx);
        git_hash_free_ctx(ctx);
 
        return 0;
 }
 
+int git_odb__hashfd_filtered(
+       git_oid *out, git_file fd, size_t size, git_otype type, git_vector *filters)
+{
+       int error;
+       git_buf raw = GIT_BUF_INIT;
+       git_buf filtered = GIT_BUF_INIT;
+
+       if (!filters || !filters->length)
+               return git_odb__hashfd(out, fd, size, type);
+
+       /* size of data is used in header, so we have to read the whole file
+        * into memory to apply filters before beginning to calculate the hash
+        */
+
+       if (!(error = git_futils_readbuffer_fd(&raw, fd, size)))
+               error = git_filters_apply(&filtered, &raw, filters);
+
+       git_buf_free(&raw);
+
+       if (!error)
+               error = git_odb_hash(out, filtered.ptr, filtered.size, type);
+
+       git_buf_free(&filtered);
+
+       return error;
+}
+
 int git_odb__hashlink(git_oid *out, const char *path)
 {
        struct stat st;
@@ -159,10 +196,11 @@ int git_odb__hashlink(git_oid *out, const char *path)
                char *link_data;
                ssize_t read_len;
 
-               link_data = git__malloc((size_t)size);
+               link_data = git__malloc((size_t)(size + 1));
                GITERR_CHECK_ALLOC(link_data);
 
-               read_len = p_readlink(path, link_data, (size_t)(size + 1));
+               read_len = p_readlink(path, link_data, (size_t)size);
+               link_data[size] = '\0';
                if (read_len != (ssize_t)size) {
                        giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
                        return -1;
@@ -170,7 +208,7 @@ int git_odb__hashlink(git_oid *out, const char *path)
 
                result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
                git__free(link_data);
-       } else { 
+       } else {
                int fd = git_futils_open_ro(path);
                if (fd < 0)
                        return -1;
@@ -483,20 +521,37 @@ int git_odb_exists(git_odb *db, const git_oid *id)
 }
 
 int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
+{
+       int error;
+       git_odb_object *object;
+
+       error = git_odb__read_header_or_object(&object, len_p, type_p, db, id);
+
+       if (object)
+               git_odb_object_free(object);
+
+       return error;
+}
+
+int git_odb__read_header_or_object(
+       git_odb_object **out, size_t *len_p, git_otype *type_p,
+       git_odb *db, const git_oid *id)
 {
        unsigned int i;
        int error = GIT_ENOTFOUND;
        git_odb_object *object;
 
-       assert(db && id);
+       assert(db && id && out && len_p && type_p);
 
        if ((object = git_cache_get(&db->cache, id)) != NULL) {
                *len_p = object->raw.len;
                *type_p = object->raw.type;
-               git_odb_object_free(object);
+               *out = object;
                return 0;
        }
 
+       *out = NULL;
+
        for (i = 0; i < db->backends.length && error < 0; ++i) {
                backend_internal *internal = git_vector_get(&db->backends, i);
                git_odb_backend *b = internal->backend;
@@ -517,7 +572,8 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
 
        *len_p = object->raw.len;
        *type_p = object->raw.type;
-       git_odb_object_free(object);
+       *out = object;
+
        return 0;
 }
 
@@ -553,12 +609,13 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
 }
 
 int git_odb_read_prefix(
-       git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
+       git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
 {
        unsigned int i;
        int error = GIT_ENOTFOUND;
        git_oid found_full_oid = {{0}};
        git_rawobj raw;
+       void *data = NULL;
        bool found = false;
 
        assert(out && db);
@@ -588,6 +645,8 @@ int git_odb_read_prefix(
                        if (error)
                                return error;
 
+                       git__free(data);
+                       data = raw.data;
                        if (found && git_oid_cmp(&full_oid, &found_full_oid))
                                return git_odb__error_ambiguous("multiple matches for prefix");
                        found_full_oid = full_oid;
@@ -602,6 +661,21 @@ int git_odb_read_prefix(
        return 0;
 }
 
+int git_odb_foreach(git_odb *db, int (*cb)(git_oid *oid, void *data), void *data)
+{
+       unsigned int i;
+       backend_internal *internal;
+
+       git_vector_foreach(&db->backends, i, internal) {
+               git_odb_backend *b = internal->backend;
+               int error = b->foreach(b, cb, data);
+               if (error < 0)
+                       return error;
+       }
+
+       return 0;
+}
+
 int git_odb_write(
        git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
 {
@@ -689,6 +763,12 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi
        return error;
 }
 
+void * git_odb_backend_malloc(git_odb_backend *backend, size_t len)
+{
+       GIT_UNUSED(backend);
+       return git__malloc(len);
+}
+
 int git_odb__error_notfound(const char *message, const git_oid *oid)
 {
        if (oid != NULL) {