1303095Ssobomax/*
2303095Ssobomax * Copyright (c) 2016 Maxim Sobolev <sobomax@FreeBSD.org>
3303095Ssobomax * All rights reserved.
4303095Ssobomax *
5303095Ssobomax * Redistribution and use in source and binary forms, with or without
6303095Ssobomax * modification, are permitted provided that the following conditions
7303095Ssobomax * are met:
8303095Ssobomax * 1. Redistributions of source code must retain the above copyright
9303095Ssobomax *    notice, this list of conditions and the following disclaimer.
10303095Ssobomax * 2. Redistributions in binary form must reproduce the above copyright
11303095Ssobomax *    notice, this list of conditions and the following disclaimer in the
12303095Ssobomax *    documentation and/or other materials provided with the distribution.
13303095Ssobomax *
14303095Ssobomax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15303095Ssobomax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16303095Ssobomax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17303095Ssobomax * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18303095Ssobomax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19303095Ssobomax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20303095Ssobomax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21303095Ssobomax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22303095Ssobomax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23303095Ssobomax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24303095Ssobomax * SUCH DAMAGE.
25303095Ssobomax */
26303095Ssobomax
27303095Ssobomax#include <sys/cdefs.h>
28303095Ssobomax__FBSDID("$FreeBSD$");
29303095Ssobomax
30303095Ssobomax#include <err.h>
31303095Ssobomax#include <stdio.h>
32303095Ssobomax#include <stdlib.h>
33303095Ssobomax#include <string.h>
34303095Ssobomax#include <unistd.h>
35303095Ssobomax
36303095Ssobomax#if defined(MKUZ_DEBUG)
37303095Ssobomax# include <assert.h>
38303095Ssobomax# include <stdio.h>
39303095Ssobomax#endif
40303095Ssobomax
41303095Ssobomax#include "mkuz_blockcache.h"
42303095Ssobomax#include "mkuz_blk.h"
43303095Ssobomax
44303095Ssobomaxstruct mkuz_blkcache_itm {
45303095Ssobomax    struct mkuz_blk_info hit;
46303095Ssobomax    struct mkuz_blkcache_itm *next;
47303095Ssobomax};
48303095Ssobomax
49303095Ssobomaxstatic struct mkuz_blkcache {
50303095Ssobomax    struct mkuz_blkcache_itm first[256];
51303095Ssobomax} blkcache;
52303095Ssobomax
53303095Ssobomaxstatic int
54303095Ssobomaxverify_match(int fd, const struct mkuz_blk *cbp, struct mkuz_blkcache_itm *bcep)
55303095Ssobomax{
56303095Ssobomax    void *vbuf;
57303095Ssobomax    ssize_t rlen;
58303095Ssobomax    int rval;
59303095Ssobomax
60303095Ssobomax    rval = -1;
61303095Ssobomax    vbuf = malloc(cbp->info.len);
62303095Ssobomax    if (vbuf == NULL) {
63303095Ssobomax        goto e0;
64303095Ssobomax    }
65303095Ssobomax    if (lseek(fd, bcep->hit.offset, SEEK_SET) < 0) {
66303095Ssobomax        goto e1;
67303095Ssobomax    }
68303095Ssobomax    rlen = read(fd, vbuf, cbp->info.len);
69303095Ssobomax    if (rlen < 0 || (unsigned)rlen != cbp->info.len) {
70303095Ssobomax        goto e2;
71303095Ssobomax    }
72303095Ssobomax    rval = (memcmp(cbp->data, vbuf, cbp->info.len) == 0) ? 1 : 0;
73303095Ssobomaxe2:
74303095Ssobomax    lseek(fd, cbp->info.offset, SEEK_SET);
75303095Ssobomaxe1:
76303095Ssobomax    free(vbuf);
77303095Ssobomaxe0:
78303095Ssobomax    return (rval);
79303095Ssobomax}
80303095Ssobomax
81303095Ssobomax#define I2J(x)	((intmax_t)(x))
82303095Ssobomax#define U2J(x)	((uintmax_t)(x))
83303095Ssobomax
84303095Ssobomaxstatic unsigned char
85303095Ssobomaxdigest_fold(const unsigned char *mdigest)
86303095Ssobomax{
87303095Ssobomax    int i;
88303095Ssobomax    unsigned char rval;
89303095Ssobomax
90303095Ssobomax    rval = mdigest[0];
91303095Ssobomax    for (i = 1; i < 16; i++) {
92303095Ssobomax        rval = rval ^ mdigest[i];
93303095Ssobomax    }
94303095Ssobomax    return (rval);
95303095Ssobomax}
96303095Ssobomax
97303095Ssobomaxstruct mkuz_blk_info *
98303095Ssobomaxmkuz_blkcache_regblock(int fd, const struct mkuz_blk *bp)
99303095Ssobomax{
100303095Ssobomax    struct mkuz_blkcache_itm *bcep;
101303095Ssobomax    int rval;
102303095Ssobomax    unsigned char h;
103303095Ssobomax
104303095Ssobomax#if defined(MKUZ_DEBUG)
105303095Ssobomax    assert((unsigned)lseek(fd, 0, SEEK_CUR) == bp->info.offset);
106303095Ssobomax#endif
107303095Ssobomax    h = digest_fold(bp->info.digest);
108303095Ssobomax    if (blkcache.first[h].hit.len == 0) {
109303095Ssobomax        bcep = &blkcache.first[h];
110303095Ssobomax    } else {
111303095Ssobomax        for (bcep = &blkcache.first[h]; bcep != NULL; bcep = bcep->next) {
112303095Ssobomax            if (bcep->hit.len != bp->info.len)
113303095Ssobomax                continue;
114303095Ssobomax            if (memcmp(bp->info.digest, bcep->hit.digest,
115303095Ssobomax              sizeof(bp->info.digest)) == 0) {
116303095Ssobomax                break;
117303095Ssobomax            }
118303095Ssobomax        }
119303095Ssobomax        if (bcep != NULL) {
120303095Ssobomax            rval = verify_match(fd, bp, bcep);
121303095Ssobomax            if (rval == 1) {
122303095Ssobomax#if defined(MKUZ_DEBUG)
123303095Ssobomax                fprintf(stderr, "cache hit %jd, %jd, %jd, %jd\n",
124303095Ssobomax                  I2J(bcep->hit.blkno), I2J(bcep->hit.offset),
125303095Ssobomax                  I2J(bp->info.offset), I2J(bp->info.len));
126303095Ssobomax#endif
127303095Ssobomax                return (&bcep->hit);
128303095Ssobomax            }
129303095Ssobomax            if (rval == 0) {
130303095Ssobomax#if defined(MKUZ_DEBUG)
131303095Ssobomax                fprintf(stderr, "block MD5 collision, you should try lottery, "
132303095Ssobomax                  "man!\n");
133303095Ssobomax#endif
134303095Ssobomax                return (NULL);
135303095Ssobomax            }
136303095Ssobomax            warn("verify_match");
137303095Ssobomax            return (NULL);
138303095Ssobomax        }
139303095Ssobomax        bcep = malloc(sizeof(struct mkuz_blkcache_itm));
140303095Ssobomax        if (bcep == NULL)
141303095Ssobomax            return (NULL);
142303095Ssobomax        memset(bcep, '\0', sizeof(struct mkuz_blkcache_itm));
143303095Ssobomax        bcep->next = blkcache.first[h].next;
144303095Ssobomax        blkcache.first[h].next = bcep;
145303095Ssobomax    }
146303095Ssobomax    bcep->hit = bp->info;
147303095Ssobomax    return (NULL);
148303095Ssobomax}
149