/* * * 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 #include #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); }