/*
*
* Copyright (C) 2007 François Pesce : francois.pesce (at) gmail (dot) com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <apr_file_io.h>
#include <apr_mmap.h>
#include "checksum.h"
#include "debug.h"
#include "ft_file.h"
static apr_status_t checksum_big_file(const char *filename, apr_off_t size, apr_uint32_t *state, apr_pool_t *gc_pool);
static apr_status_t big_filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, int *i);
/*#define HUGE_LEN 8192*/
#define HUGE_LEN 4096
#define MIN(a,b) ((a)<(b)) ? (a) : (b)
static apr_status_t checksum_small_file(const char *filename, apr_off_t size, apr_uint32_t *state, apr_pool_t *gc_pool)
{
char errbuf[128];
apr_file_t *fd = NULL;
apr_mmap_t *mm;
apr_status_t status;
apr_uint32_t i;
apr_off_t rbytes;
status = apr_file_open(&fd, filename, APR_READ | APR_BINARY, APR_OS_DEFAULT, gc_pool);
if (APR_SUCCESS != status) {
return status;
}
status = apr_mmap_create(&mm, fd, 0, (apr_size_t) size, APR_MMAP_READ, gc_pool);
if (APR_SUCCESS != status) {
apr_file_close(fd);
return checksum_big_file(filename, size, state, gc_pool);
}
for (i = 0; i < HASHSTATE; ++i)
state[i] = 1;
rbytes = 0;
do {
hash(mm->mm + rbytes, MIN(HUGE_LEN, size), state);
rbytes += MIN(HUGE_LEN, size);
size -= HUGE_LEN;
} while (size > 0);
if (APR_SUCCESS != (status = apr_mmap_delete(mm))) {
DEBUG_ERR("error calling apr_mmap_delete: %s", apr_strerror(status, errbuf, 128));
apr_file_close(fd);
return status;
}
if (APR_SUCCESS != (status = apr_file_close(fd))) {
DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, 128));
return status;
}
return APR_SUCCESS;
}
static apr_status_t checksum_big_file(const char *filename, apr_off_t size, apr_uint32_t *state, apr_pool_t *gc_pool)
{
unsigned char data_chunk[HUGE_LEN];
char errbuf[128];
apr_size_t rbytes;
apr_file_t *fd = NULL;
apr_status_t status;
apr_uint32_t i;
status = apr_file_open(&fd, filename, APR_READ | APR_BINARY, APR_OS_DEFAULT, gc_pool);
if (APR_SUCCESS != status) {
return status;
}
for (i = 0; i < HASHSTATE; ++i)
state[i] = 1;
do {
rbytes = HUGE_LEN;
status = apr_file_read(fd, data_chunk, &rbytes);
if (APR_SUCCESS == status) {
hash(data_chunk, rbytes, state);
}
} while (APR_SUCCESS == status);
if (APR_EOF != status) {
DEBUG_ERR("unable to read(%s, O_RDONLY), skipping: %s", filename, apr_strerror(status, errbuf, 128));
apr_file_close(fd);
return status;
}
if (APR_SUCCESS != (status = apr_file_close(fd))) {
DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, 128));
return status;
}
return APR_SUCCESS;
}
extern apr_status_t checksum_file(const char *filename, apr_off_t size, apr_off_t excess_size, apr_uint32_t *state,
apr_pool_t *gc_pool)
{
if (size < excess_size)
return checksum_small_file(filename, size, state, gc_pool);
return checksum_big_file(filename, size, state, gc_pool);
}
static apr_status_t small_filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, int *i)
{
char errbuf[128];
apr_file_t *fd1 = NULL, *fd2 = NULL;
apr_mmap_t *mm1, *mm2;
apr_status_t status;
if (0 == size) {
*i = 0;
return APR_SUCCESS;
}
status = apr_file_open(&fd1, fname1, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
if (APR_SUCCESS != status) {
return status;
}
status = apr_mmap_create(&mm1, fd1, 0, (apr_size_t) size, APR_MMAP_READ, pool);
if (APR_SUCCESS != status) {
apr_file_close(fd1);
return big_filecmp(pool, fname1, fname2, size, i);
}
status = apr_file_open(&fd2, fname2, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
if (APR_SUCCESS != status) {
apr_mmap_delete(mm1);
apr_file_close(fd1);
return status;
}
status = apr_mmap_create(&mm2, fd2, 0, size, APR_MMAP_READ, pool);
if (APR_SUCCESS != status) {
apr_file_close(fd2);
apr_mmap_delete(mm1);
apr_file_close(fd1);
return big_filecmp(pool, fname1, fname2, size, i);
}
*i = memcmp(mm1->mm, mm2->mm, size);
if (APR_SUCCESS != (status = apr_mmap_delete(mm2))) {
DEBUG_ERR("error calling apr_mmap_delete: %s", apr_strerror(status, errbuf, 128));
apr_file_close(fd2);
apr_mmap_delete(mm1);
apr_file_close(fd1);
return status;
}
if (APR_SUCCESS != (status = apr_file_close(fd2))) {
DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, 128));
apr_mmap_delete(mm1);
apr_file_close(fd1);
return status;
}
if (APR_SUCCESS != (status = apr_mmap_delete(mm1))) {
DEBUG_ERR("error calling apr_mmap_delete: %s", apr_strerror(status, errbuf, 128));
apr_file_close(fd1);
return status;
}
if (APR_SUCCESS != (status = apr_file_close(fd1))) {
DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, 128));
return status;
}
return APR_SUCCESS;
}
static apr_status_t big_filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, int *i)
{
unsigned char data_chunk1[HUGE_LEN], data_chunk2[HUGE_LEN];
char errbuf[128];
apr_size_t rbytes1, rbytes2;
apr_file_t *fd1 = NULL, *fd2 = NULL;
apr_status_t status1, status2;
if (0 == size) {
*i = 0;
return APR_SUCCESS;
}
status1 = apr_file_open(&fd1, fname1, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
if (APR_SUCCESS != status1) {
return status1;
}
status1 = apr_file_open(&fd2, fname2, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
if (APR_SUCCESS != status1) {
apr_file_close(fd1);
return status1;
}
do {
rbytes1 = HUGE_LEN;
status1 = apr_file_read(fd1, data_chunk1, &rbytes1);
rbytes2 = rbytes1;
status2 = apr_file_read(fd2, data_chunk2, &rbytes2);
if ((APR_SUCCESS == status1) && (APR_SUCCESS == status2) && (rbytes2 == rbytes1)) {
*i = memcmp(data_chunk1, data_chunk2, rbytes1);
}
} while ((APR_SUCCESS == status1) && (APR_SUCCESS == status2) && (0 == *i) && (rbytes2 == rbytes1));
if ((APR_EOF != status1) && (APR_EOF != status2) && (0 == *i)) {
DEBUG_ERR("1:unable to read %s (%" APR_SIZE_T_FMT "): %s", fname1, rbytes1, apr_strerror(status1, errbuf, 128));
DEBUG_ERR("2:unable to read %s (%" APR_SIZE_T_FMT "): %s", fname2, rbytes2, apr_strerror(status2, errbuf, 128));
return status1;
}
if (APR_SUCCESS != (status1 = apr_file_close(fd2))) {
DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status1, errbuf, 128));
apr_file_close(fd1);
return status1;
}
if (APR_SUCCESS != (status1 = apr_file_close(fd1))) {
DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status1, errbuf, 128));
return status1;
}
return APR_SUCCESS;
}
extern apr_status_t filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, apr_off_t excess_size,
int *i)
{
if (size < excess_size)
return small_filecmp(pool, fname1, fname2, size, i);
return big_filecmp(pool, fname1, fname2, size, i);
}
syntax highlighted by Code2HTML, v. 0.9.1