ftwin 0.8.10
ft_file.c
Go to the documentation of this file.
1
6/*
7 *
8 * Copyright (C) 2007 François Pesce : francois.pesce (at) gmail (dot) com
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 */
22
23#include <apr_file_io.h>
24#include <apr_mmap.h>
25
26#include "checksum.h"
27#include "debug.h"
28#include "ft_file.h"
29#include "ft_config.h"
30
31static apr_status_t checksum_big_file(const char *filename, apr_off_t size, ft_hash_t *hash_out, apr_pool_t *gc_pool);
32static apr_status_t big_filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, int *result_out);
33
41static const size_t HUGE_LEN = 64 * 1024;
42
43static apr_status_t checksum_small_file(const char *filename, apr_off_t size, ft_hash_t *hash_out, apr_pool_t *gc_pool)
44{
45 char errbuf[CHAR_MAX_VAL];
46 apr_file_t *file_descriptor = NULL;
47 apr_mmap_t *memory_map = NULL;
48 memset(errbuf, 0, sizeof(errbuf));
49 apr_status_t status = APR_SUCCESS;
50
51 status = apr_file_open(&file_descriptor, filename, APR_READ | APR_BINARY, APR_OS_DEFAULT, gc_pool);
52 if (APR_SUCCESS != status) {
53 return status;
54 }
55
56 status = apr_mmap_create(&memory_map, file_descriptor, 0, (apr_size_t) size, APR_MMAP_READ, gc_pool);
57 if (APR_SUCCESS != status) {
58 (void) apr_file_close(file_descriptor);
59 return checksum_big_file(filename, size, hash_out, gc_pool);
60 }
61
62 *hash_out = XXH3_128bits(memory_map->mm, (size_t) size);
63
64 status = apr_mmap_delete(memory_map);
65 if (APR_SUCCESS != status) {
66 DEBUG_ERR("error calling apr_mmap_delete: %s", apr_strerror(status, errbuf, sizeof(errbuf)));
67 (void) apr_file_close(file_descriptor);
68 return status;
69 }
70 status = apr_file_close(file_descriptor);
71 if (APR_SUCCESS != status) {
72 DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, sizeof(errbuf)));
73 return status;
74 }
75
76 return APR_SUCCESS;
77}
78
79static apr_status_t checksum_big_file(const char *filename, apr_off_t size, ft_hash_t *hash_out, apr_pool_t *gc_pool)
80{
81 unsigned char data_chunk[HUGE_LEN];
82 char errbuf[CHAR_MAX_VAL];
83 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
84 apr_size_t rbytes;
85 apr_file_t *file_descriptor = NULL;
86 memset(data_chunk, 0, sizeof(data_chunk));
87 memset(errbuf, 0, sizeof(errbuf));
88 apr_status_t status = APR_SUCCESS;
89 XXH3_state_t *const state = XXH3_createState();
90
91 if (state == NULL) {
92 return APR_ENOMEM;
93 }
94 XXH3_128bits_reset(state);
95
96 status = apr_file_open(&file_descriptor, filename, APR_READ | APR_BINARY, APR_OS_DEFAULT, gc_pool);
97 if (APR_SUCCESS != status) {
98 XXH3_freeState(state);
99 return status;
100 }
101
102 do {
103 rbytes = HUGE_LEN;
104 status = apr_file_read(file_descriptor, data_chunk, &rbytes);
105 if ((APR_SUCCESS == status || (APR_EOF == status && rbytes > 0))) {
106 if (XXH3_128bits_update(state, data_chunk, rbytes) == XXH_ERROR) {
107 DEBUG_ERR("Error during hash update for file: %s", filename);
108 XXH3_freeState(state);
109 (void) apr_file_close(file_descriptor);
110 return APR_EGENERAL;
111 }
112 }
113 } while (APR_SUCCESS == status);
114
115 if (APR_EOF != status) {
116 DEBUG_ERR("unable to read(%s, O_RDONLY), skipping: %s", filename, apr_strerror(status, errbuf, sizeof(errbuf)));
117 XXH3_freeState(state);
118 (void) apr_file_close(file_descriptor);
119 return status;
120 }
121
122 *hash_out = XXH3_128bits_digest(state);
123 XXH3_freeState(state);
124
125 status = apr_file_close(file_descriptor);
126 if (APR_SUCCESS != status) {
127 DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, sizeof(errbuf)));
128 return status;
129 }
130
131 return APR_SUCCESS;
132}
133
134extern apr_status_t checksum_file(const char *filename, apr_off_t size, apr_off_t excess_size, ft_hash_t *hash_out,
135 apr_pool_t *gc_pool)
136{
137 if (size < excess_size) {
138 return checksum_small_file(filename, size, hash_out, gc_pool);
139 }
140
141 return checksum_big_file(filename, size, hash_out, gc_pool);
142}
143
144static apr_status_t small_filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, int *result_out)
145{
146 char errbuf[CHAR_MAX_VAL];
147 apr_file_t *fd1 = NULL;
148 apr_file_t *fd2 = NULL;
149 apr_mmap_t *mm1 = NULL;
150 apr_mmap_t *mm2 = NULL;
151 memset(errbuf, 0, sizeof(errbuf));
152 apr_status_t status = APR_SUCCESS;
153
154 if (0 == size) {
155 *result_out = 0;
156 return APR_SUCCESS;
157 }
158
159 status = apr_file_open(&fd1, fname1, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
160 if (APR_SUCCESS != status) {
161 return status;
162 }
163
164 status = apr_mmap_create(&mm1, fd1, 0, (apr_size_t) size, APR_MMAP_READ, pool);
165 if (APR_SUCCESS != status) {
166 (void) apr_file_close(fd1);
167 return big_filecmp(pool, fname1, fname2, size, result_out);
168 }
169
170 status = apr_file_open(&fd2, fname2, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
171 if (APR_SUCCESS != status) {
172 apr_mmap_delete(mm1);
173 (void) apr_file_close(fd1);
174 return status;
175 }
176
177 status = apr_mmap_create(&mm2, fd2, 0, size, APR_MMAP_READ, pool);
178 if (APR_SUCCESS != status) {
179 (void) apr_file_close(fd2);
180 (void) apr_file_close(fd2);
181 (void) apr_file_close(fd1);
182 return big_filecmp(pool, fname1, fname2, size, result_out);
183 }
184
185 *result_out = memcmp(mm1->mm, mm2->mm, size);
186
187 status = apr_mmap_delete(mm2);
188 if (APR_SUCCESS != status) {
189 DEBUG_ERR("error calling apr_mmap_delete: %s", apr_strerror(status, errbuf, sizeof(errbuf)));
190 (void) apr_file_close(fd2);
191 (void) apr_mmap_delete(mm1);
192 (void) apr_file_close(fd1);
193 return status;
194 }
195 status = apr_file_close(fd2);
196 if (APR_SUCCESS != status) {
197 DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, sizeof(errbuf)));
198 (void) apr_mmap_delete(mm1);
199 (void) apr_file_close(fd1);
200 return status;
201 }
202
203 status = apr_mmap_delete(mm1);
204 if (APR_SUCCESS != status) {
205 DEBUG_ERR("error calling apr_mmap_delete: %s", apr_strerror(status, errbuf, sizeof(errbuf)));
206 (void) apr_file_close(fd1);
207 return status;
208 }
209 status = apr_file_close(fd1);
210 if (APR_SUCCESS != status) {
211 DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status, errbuf, sizeof(errbuf)));
212 return status;
213 }
214
215 return APR_SUCCESS;
216}
217
218static apr_status_t big_filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, int *result_out)
219{
220 unsigned char data_chunk1[HUGE_LEN];
221 unsigned char data_chunk2[HUGE_LEN];
222 char errbuf[CHAR_MAX_VAL];
223 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
224 apr_size_t rbytes1;
225 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
226 apr_size_t rbytes2;
227 apr_file_t *fd1 = NULL;
228 apr_file_t *fd2 = NULL;
229 memset(data_chunk1, 0, sizeof(data_chunk1));
230 memset(data_chunk2, 0, sizeof(data_chunk2));
231 memset(errbuf, 0, sizeof(errbuf));
232 apr_status_t status1 = APR_SUCCESS;
233 apr_status_t status2 = APR_SUCCESS;
234
235 if (0 == size) {
236 *result_out = 0;
237 return APR_SUCCESS;
238 }
239
240 status1 = apr_file_open(&fd1, fname1, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
241 if (APR_SUCCESS != status1) {
242 return status1;
243 }
244
245 status1 = apr_file_open(&fd2, fname2, APR_READ | APR_BINARY, APR_OS_DEFAULT, pool);
246 if (APR_SUCCESS != status1) {
247 (void) apr_file_close(fd1);
248 return status1;
249 }
250
251 do {
252 rbytes1 = HUGE_LEN;
253 status1 = apr_file_read(fd1, data_chunk1, &rbytes1);
254 rbytes2 = rbytes1;
255 status2 = apr_file_read(fd2, data_chunk2, &rbytes2);
256 if ((APR_SUCCESS == status1) && (APR_SUCCESS == status2) && (rbytes2 == rbytes1)) {
257 *result_out = memcmp(data_chunk1, data_chunk2, rbytes1);
258 }
259 } while ((APR_SUCCESS == status1) && (APR_SUCCESS == status2) && (0 == *result_out) && (rbytes2 == rbytes1));
260
261 if ((APR_EOF != status1) && (APR_EOF != status2) && (0 == *result_out)) {
262 DEBUG_ERR("1:unable to read %s (%" APR_SIZE_T_FMT "): %s", fname1, rbytes1,
263 apr_strerror(status1, errbuf, sizeof(errbuf)));
264 DEBUG_ERR("2:unable to read %s (%" APR_SIZE_T_FMT "): %s", fname2, rbytes2,
265 apr_strerror(status2, errbuf, sizeof(errbuf)));
266 return status1;
267 }
268
269 status1 = apr_file_close(fd2);
270 if (APR_SUCCESS != status1) {
271 DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status1, errbuf, sizeof(errbuf)));
272 (void) apr_file_close(fd1);
273 return status1;
274 }
275
276 status1 = apr_file_close(fd1);
277 if (APR_SUCCESS != status1) {
278 DEBUG_ERR("error calling apr_file_close: %s", apr_strerror(status1, errbuf, sizeof(errbuf)));
279 return status1;
280 }
281
282 return APR_SUCCESS;
283}
284
285extern apr_status_t filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, apr_off_t excess_size,
286 int *result_out)
287{
288 if (size < excess_size) {
289 return small_filecmp(pool, fname1, fname2, size, result_out);
290 }
291
292 return big_filecmp(pool, fname1, fname2, size, result_out);
293}
Defines the core checksum type used throughout the application.
UTIL debug output macros.
#define DEBUG_ERR(str, arg...)
Display error message at the level error.
Definition debug.h:31
static const size_t HUGE_LEN
The chunk size for processing large files.
Definition ft_file.c:41
apr_status_t checksum_file(const char *filename, apr_off_t size, apr_off_t excess_size, ft_hash_t *hash_out, apr_pool_t *gc_pool)
Calculates the XXH128 checksum of a file.
Definition ft_file.c:134
apr_status_t filecmp(apr_pool_t *pool, const char *fname1, const char *fname2, apr_off_t size, apr_off_t excess_size, int *result_out)
Compares two files byte-by-byte to determine if they are identical.
Definition ft_file.c:285
Interface for file comparison and checksum calculation.
static const size_t CHAR_MAX_VAL
The maximum value of a character, often used as a buffer size.
Definition ft_config.h:35
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t *statePtr)
Resets an XXH3_state_t to begin a new hash.
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_digest(XXH_NOESCAPE const XXH3_state_t *statePtr)
Returns the calculated XXH3 128-bit hash value from an XXH3_state_t.
struct XXH3_state_s XXH3_state_t
The state struct for the XXH3 streaming API.
Definition xxhash.h:1130
XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void *data, size_t len)
Unseeded 128-bit variant of XXH3.
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update(XXH_NOESCAPE XXH3_state_t *statePtr, XXH_NOESCAPE const void *input, size_t length)
Consumes a block of input to an XXH3_state_t.
@ XXH_ERROR
Definition xxhash.h:502
The return value from 128-bit hashes.
Definition xxhash.h:1239