properly set errno
[~helmut/ssdeep.git] / fuzzy.c
diff --git a/fuzzy.c b/fuzzy.c
index a6e238c..a9e6d4a 100644 (file)
--- a/fuzzy.c
+++ b/fuzzy.c
@@ -23,6 +23,7 @@
  */
 
 #include <assert.h>
+#include <errno.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -90,19 +91,17 @@ static uint32_t sum_hash(unsigned char c, uint32_t h) {
 }
 
 /* A blockhash contains a signature state for a specific (implicit) blocksize.
- * The blocksize is given by the start_blocksize from ssdeep_context times two
- * to the power of the position of the element in the blockhashes member of
- * ssdeep_context (head position = 0). The h and halfh members are the FNV
- * hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2 long.
- * The halfh hash is needed be able to truncate digest for the second output
- * hash to stay compatible with ssdeep output. */
+ * The blocksize is given by SSDEEP_BS(index). The h and halfh members are the
+ * FNV hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2
+ * long. The halfh hash is needed be able to truncate digest for the second
+ * output hash to stay compatible with ssdeep output. */
 struct blockhash_context {
        uint32_t h, halfh;
        char digest[SPAMSUM_LENGTH];
        unsigned int dlen;
 };
 
-struct ssdeep_context {
+struct fuzzy_state {
        unsigned int bhstart, bhend;
        struct blockhash_context bh[NUM_BLOCKHASHES];
        size_t total_size;
@@ -111,9 +110,10 @@ struct ssdeep_context {
 
 #define SSDEEP_BS(index) (((uint32_t)MIN_BLOCKSIZE) << (index))
 
-static /*@only@*/ /*@null@*/ struct ssdeep_context *ssdeep_new(void) {
-       struct ssdeep_context *self;
-       if(NULL == (self = malloc(sizeof(struct ssdeep_context))))
+/*@only@*/ /*@null@*/ struct fuzzy_state *fuzzy_new(void) {
+       struct fuzzy_state *self;
+       if(NULL == (self = malloc(sizeof(struct fuzzy_state))))
+               /* malloc sets ENOMEM */
                return NULL;
        self->bhstart = 0;
        self->bhend = 1;
@@ -125,7 +125,7 @@ static /*@only@*/ /*@null@*/ struct ssdeep_context *ssdeep_new(void) {
        return self;
 }
 
-static void ssdeep_try_fork_blockhash(struct ssdeep_context *self) {
+static void fuzzy_try_fork_blockhash(struct fuzzy_state *self) {
        struct blockhash_context *obh, *nbh;
        if(self->bhend >= NUM_BLOCKHASHES)
                return;
@@ -138,7 +138,7 @@ static void ssdeep_try_fork_blockhash(struct ssdeep_context *self) {
        ++self->bhend;
 }
 
-static void ssdeep_try_reduce_blockhash(struct ssdeep_context *self) {
+static void fuzzy_try_reduce_blockhash(struct fuzzy_state *self) {
        assert(self->bhstart < self->bhend);
        if(self->bhend - self->bhstart < 2)
                /* Need at least two working hashes. */
@@ -159,7 +159,7 @@ static void ssdeep_try_reduce_blockhash(struct ssdeep_context *self) {
 static const char *b64 =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-static void ssdeep_engine_step(struct ssdeep_context *self, unsigned char c) {
+static void fuzzy_engine_step(struct fuzzy_state *self, unsigned char c) {
        size_t h;
        unsigned int i;
        /* At each character we update the rolling hash and the normal hashes.
@@ -186,7 +186,7 @@ static void ssdeep_engine_step(struct ssdeep_context *self, unsigned char c) {
                if(unlikely(0 == self->bh[i].dlen)) {
                        /* Can only happen 30 times. */
                        /* First step for this blocksize. Clone next. */
-                       ssdeep_try_fork_blockhash(self);
+                       fuzzy_try_fork_blockhash(self);
                }
                if(self->bh[i].dlen < SPAMSUM_LENGTH - 1) {
                        /* We can have a problem with the tail overflowing. The
@@ -201,20 +201,19 @@ static void ssdeep_engine_step(struct ssdeep_context *self, unsigned char c) {
                        if(self->bh[i].dlen < SPAMSUM_LENGTH / 2)
                                self->bh[i].halfh = HASH_INIT;
                } else
-                       ssdeep_try_reduce_blockhash(self);
+                       fuzzy_try_reduce_blockhash(self);
        }
 }
 
-static int ssdeep_engine(struct ssdeep_context *self,
-               const unsigned char *buffer, size_t buffer_size) {
+int fuzzy_update(struct fuzzy_state *self, const unsigned char *buffer,
+               size_t buffer_size) {
        self->total_size += buffer_size;
        for( ;buffer_size > 0; ++buffer, --buffer_size)
-               ssdeep_engine_step(self, *buffer);
+               fuzzy_engine_step(self, *buffer);
        return 0;
 }
 
-static int ssdeep_digest(const struct ssdeep_context *self,
-               /*@out@*/ char *result) {
+int fuzzy_digest(const struct fuzzy_state *self, /*@out@*/ char *result) {
        unsigned int bi = self->bhstart;
        uint32_t h = roll_sum(&self->roll);
        int i, remain = FUZZY_MAX_RESULT - 1;
@@ -225,17 +224,22 @@ static int ssdeep_digest(const struct ssdeep_context *self,
        /* Initial blocksize guess. */
        while((size_t)SSDEEP_BS(bi) * SPAMSUM_LENGTH < self->total_size) {
                ++bi;
-               if(bi >= self->bhend)
+               if(bi >= NUM_BLOCKHASHES) {
                        /* The input exceeds data types. */
+                       errno = EOVERFLOW;
                        return -1;
+               }
        }
        /* Adapt blocksize guess to actual digest length. */
+       while(bi >= self->bhend)
+               --bi;
        while(bi > self->bhstart && self->bh[bi].dlen < SPAMSUM_LENGTH / 2)
                --bi;
        assert(!(bi > 0 && self->bh[bi].dlen < SPAMSUM_LENGTH / 2));
 
        i = snprintf(result, (size_t)remain, "%u:", SSDEEP_BS(bi));
        if(i <= 0)
+               /* Maybe snprintf has set errno here? */
                return -1;
        assert(i < remain);
        remain -= i;
@@ -277,47 +281,47 @@ static int ssdeep_digest(const struct ssdeep_context *self,
        return 0;
 }
 
-static void ssdeep_free(/*@only@*/ struct ssdeep_context *self) {
+void fuzzy_free(/*@only@*/ struct fuzzy_state *self) {
        free(self);
 }
 
 int fuzzy_hash_buf(const unsigned char *buf, uint32_t buf_len,
                /*@out@*/ char *result) {
-       struct ssdeep_context *ctx;
+       struct fuzzy_state *ctx;
        int ret = -1;
-       if(NULL == (ctx = ssdeep_new()))
+       if(NULL == (ctx = fuzzy_new()))
                return -1;
-       if(ssdeep_engine(ctx, buf, buf_len) < 0)
+       if(fuzzy_update(ctx, buf, buf_len) < 0)
                goto out;
-       if(ssdeep_digest(ctx, result) < 0)
+       if(fuzzy_digest(ctx, result) < 0)
                goto out;
        ret = 0;
 out:
-       ssdeep_free(ctx);
+       fuzzy_free(ctx);
        return ret;
 }
 
 int fuzzy_hash_stream(FILE *handle, /*@out@*/ char *result) {
-       struct ssdeep_context *ctx;
+       struct fuzzy_state *ctx;
        unsigned char buffer[4096];
        size_t n;
        int ret = -1;
-       if(NULL == (ctx = ssdeep_new()))
+       if(NULL == (ctx = fuzzy_new()))
                return -1;
        for(;;) {
                n = fread(buffer, 1, 4096, handle);
                if(0 == n)
                        break;
-               if(ssdeep_engine(ctx, buffer, n) < 0)
+               if(fuzzy_update(ctx, buffer, n) < 0)
                        goto out;
        }
        if(ferror(handle) != 0)
                goto out;
-       if(ssdeep_digest(ctx, result) < 0)
+       if(fuzzy_digest(ctx, result) < 0)
                goto out;
        ret = 0;
 out:
-       ssdeep_free(ctx);
+       fuzzy_free(ctx);
        return ret;
 }