"Fossies" - the Fresh Open Source Software Archive 
Member "geoipupdate-3.1.1/bin/functions.c" (23 Aug 2018, 12493 Bytes) of package /linux/misc/geoipupdate-3.1.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "functions.c" see the
Fossies "Dox" file reference documentation.
1 #include "functions.h"
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <stdbool.h>
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 static ssize_t read_file(char const *const, void *const, size_t const);
17
18 // Return the length of the given string in bytes. Look at at most maxlen
19 // bytes.
20 //
21 // This is intended to behave like strnlen(3). We don't use strnlen(3) as it is
22 // not part of C99.
23 size_t gu_strnlen(char const *const s, size_t const maxlen) {
24 if (!s) {
25 return 0;
26 }
27
28 char const *ptr = s;
29 size_t n = 0;
30 while (n < maxlen && *ptr != '\0') {
31 n++;
32 ptr++;
33 }
34
35 return n;
36 }
37
38 // Check whether the file looks like a valid gzip file.
39 //
40 // We open it and read in a small amount. We do this so we can check its header.
41 //
42 // Return true if it is.
43 bool is_valid_gzip_file(char const *const file) {
44 if (file == NULL || strlen(file) == 0) {
45 fprintf(stderr, "is_valid_gzip_file: %s\n", strerror(EINVAL));
46 return false;
47 }
48
49 size_t const bufsz = 2;
50
51 uint8_t *const buf = calloc(bufsz, sizeof(uint8_t));
52 if (buf == NULL) {
53 fprintf(stderr, "is_valid_gzip_file: %s\n", strerror(errno));
54 return false;
55 }
56
57 ssize_t const sz = read_file(file, buf, bufsz);
58 if (sz == -1) {
59 // We should have already reported an error.
60 free(buf);
61 return false;
62 }
63
64 if ((size_t const)sz != bufsz) {
65 fprintf(
66 stderr, "%s is not a valid gzip file (due to file size)\n", file);
67 free(buf);
68 return false;
69 }
70
71 if (buf[0] != 0x1f || buf[1] != 0x8b) {
72 fprintf(stderr, "%s is not a valid gzip file\n", file);
73 free(buf);
74 return false;
75 }
76
77 free(buf);
78
79 return true;
80 }
81
82 // Read in up to 8 KiB of a file.
83 //
84 // If you don't care how much of the file you read in, this function is easier
85 // to use than read_file().
86 //
87 // I chose 8 KiB arbitrarily.
88 //
89 // We guarantee the returned buffer will terminate with a NUL byte and be
90 // exactly 8 KiB. The useful portion may fall short of 8 KiB.
91 //
92 // The caller is responsible for the returned memory.
93 char *slurp_file(char const *const file) {
94 if (file == NULL || strlen(file) == 0) {
95 fprintf(stderr, "slurp_file: %s\n", strerror(EINVAL));
96 return NULL;
97 }
98
99 size_t sz = 8193;
100
101 char *const buf = calloc(sz, sizeof(char));
102 if (buf == NULL) {
103 fprintf(stderr, "slurp_file: %s\n", strerror(errno));
104 return NULL;
105 }
106
107 ssize_t const read_sz = read_file(file, buf, sz - 1);
108 if (read_sz == -1) {
109 // We should have reported an error.
110 free(buf);
111 return NULL;
112 }
113
114 return buf;
115 }
116
117 // Read in up to the first sz bytes of a file.
118 //
119 // Return how many bytes we read. -1 if there was an error.
120 //
121 // The buffer may or may not contain a string. It may be binary data.
122 static ssize_t
123 read_file(char const *const file, void *const buf, size_t const bufsz) {
124 if (file == NULL || strlen(file) == 0 || buf == NULL || bufsz == 0) {
125 fprintf(stderr, "read_file: %s\n", strerror(EINVAL));
126 return -1;
127 }
128
129 // Note previously we used fopen() and getline() to read, but getline() is
130 // not appropriate when we have a binary file such as gzip. It reads until
131 // it finds a newline and will resize the buffer if necessary. Use read(2)
132 // instead.
133
134 int const fd = open(file, O_RDONLY);
135 if (fd == -1) {
136 fprintf(stderr,
137 "read_file: Can't open file: %s: %s\n",
138 file,
139 strerror(errno));
140 return -1;
141 }
142
143 ssize_t total_read_bytes = 0;
144 int retries_remaining = 3;
145
146 while (1) {
147 size_t const bytes_left_to_read = bufsz - total_read_bytes;
148 if (bytes_left_to_read == 0) {
149 break;
150 }
151
152 if (retries_remaining == 0) {
153 fprintf(
154 stderr,
155 "read_file: Interrupted when reading from %s too many times\n",
156 file);
157 close(fd);
158 return -1;
159 }
160
161 ssize_t const read_bytes =
162 read(fd, buf + total_read_bytes, bytes_left_to_read);
163 if (read_bytes < 0) {
164 if (errno == EINTR) {
165 retries_remaining--;
166 continue;
167 }
168 fprintf(stderr,
169 "read_file: Error reading from %s: %s\n",
170 file,
171 strerror(errno));
172 close(fd);
173 return -1;
174 }
175
176 // EOF.
177 if (read_bytes == 0) {
178 break;
179 }
180
181 if (total_read_bytes > SSIZE_MAX - read_bytes) {
182 fprintf(stderr,
183 "read_file: Overflow when counting number of read bytes\n");
184 close(fd);
185 return -1;
186 }
187
188 total_read_bytes += read_bytes;
189 }
190
191 if (close(fd) != 0) {
192 fprintf(stderr,
193 "read_file: Error closing file: %s: %s\n",
194 file,
195 strerror(errno));
196 return -1;
197 }
198
199 return total_read_bytes;
200 }
201
202 #ifdef TEST_FUNCTIONS
203
204 #include <assert.h>
205
206 static void test_gu_strnlen(void);
207 static void test_is_valid_gzip_file(void);
208 static void test_slurp_file(void);
209 static void test_read_file(void);
210 static char *get_temporary_filename(void);
211 static void write_file(char const *const, void const *const, size_t const);
212
213 int main(void) {
214 test_gu_strnlen();
215 test_is_valid_gzip_file();
216 test_slurp_file();
217 test_read_file();
218
219 return 0;
220 }
221
222 static void test_gu_strnlen(void) {
223 struct test_case {
224 char const *const s;
225 size_t const maxlen;
226 size_t const output;
227 bool const skip_strnlen;
228 };
229
230 struct test_case const tests[] = {
231 {
232 .s = "test",
233 .maxlen = 4,
234 .output = 4,
235 .skip_strnlen = false,
236 },
237 {
238 .s = "test",
239 .maxlen = 5,
240 .output = 4,
241 .skip_strnlen = false,
242 },
243 {
244 .s = "test",
245 .maxlen = 6,
246 .output = 4,
247 .skip_strnlen = false,
248 },
249 {
250 .s = "test",
251 .maxlen = 14,
252 .output = 4,
253 .skip_strnlen = false,
254 },
255 {
256 .s = "test",
257 .maxlen = 2,
258 .output = 2,
259 .skip_strnlen = false,
260 },
261 {
262 .s = "test",
263 .maxlen = 0,
264 .output = 0,
265 .skip_strnlen = false,
266 },
267 {
268 .s = "",
269 .maxlen = 4,
270 .output = 0,
271 .skip_strnlen = false,
272 },
273 {
274 .s = "",
275 .maxlen = 0,
276 .output = 0,
277 .skip_strnlen = false,
278 },
279 {
280 .s = NULL,
281 .maxlen = 0,
282 .output = 0,
283 .skip_strnlen = false,
284 },
285 {
286 .s = NULL,
287 .maxlen = 10,
288 .output = 0,
289 // segfaults strnlen
290 .skip_strnlen = true,
291 },
292 };
293
294 for (size_t i = 0; i < sizeof tests / sizeof tests[0]; i++) {
295 struct test_case const test = tests[i];
296 size_t const output = gu_strnlen(test.s, test.maxlen);
297 assert(output == test.output);
298 if (test.skip_strnlen) {
299 continue;
300 }
301 assert(output == strnlen(test.s, test.maxlen));
302 }
303 }
304
305 static void test_is_valid_gzip_file(void) {
306 char *const filename = get_temporary_filename();
307 assert(filename != NULL);
308
309 // A buffer to work with.
310 uint8_t buf[4] = {0};
311
312 // Test: File does not exist.
313
314 assert(!is_valid_gzip_file(filename));
315
316 // Test: File is too short.
317
318 memset(buf, 0, 4);
319 buf[0] = 0x1f;
320 write_file(filename, buf, 1);
321 assert(!is_valid_gzip_file(filename));
322
323 // Test: File is exactly long enough, but not a gzip file.
324
325 memset(buf, 0, 4);
326 buf[0] = 'a';
327 buf[1] = 'b';
328 write_file(filename, buf, 2);
329 assert(!is_valid_gzip_file(filename));
330
331 // Test: File is more than long enough, but not a gzip file.
332
333 memset(buf, 0, 4);
334 buf[0] = 'a';
335 buf[1] = 'b';
336 buf[3] = 'c';
337 write_file(filename, buf, 3);
338 assert(!is_valid_gzip_file(filename));
339
340 // Test: File is exactly long enough, and a gzip file.
341
342 memset(buf, 0, 4);
343 buf[0] = 0x1f;
344 buf[1] = 0x8b;
345 write_file(filename, buf, 2);
346 assert(is_valid_gzip_file(filename));
347
348 // Test: File is more than long enough, and a gzip file (at least judging
349 // by its header).
350
351 memset(buf, 0, 4);
352 buf[0] = 0x1f;
353 buf[1] = 0x8b;
354 buf[2] = 'a';
355 write_file(filename, buf, 3);
356 assert(is_valid_gzip_file(filename));
357
358 // Clean up.
359 unlink(filename);
360 free(filename);
361 }
362
363 static void test_slurp_file(void) {
364 char *const filename = get_temporary_filename();
365 assert(filename != NULL);
366
367 // Test: File does not exist.
368
369 char *const contents_0 = slurp_file(filename);
370 assert(contents_0 == NULL);
371
372 // Test: File is zero size.
373
374 write_file(filename, "", 0);
375 char *const contents_1 = slurp_file(filename);
376 assert(contents_1 != NULL);
377 assert(strlen(contents_1) == 0);
378 free(contents_1);
379
380 // Test: File has a short string.
381
382 write_file(filename, "hello", strlen("hello"));
383 char *const contents_2 = slurp_file(filename);
384 assert(contents_2 != NULL);
385 assert(strcmp(contents_2, "hello") == 0);
386 free(contents_2);
387
388 // Test: File is oversize.
389
390 char contents[8194] = {0};
391 memset(contents, 'a', 8193);
392
393 write_file(filename, contents, strlen(contents));
394
395 char expected[8193] = {0};
396 memset(expected, 'a', 8192);
397
398 char *const contents_3 = slurp_file(filename);
399 assert(contents_3 != NULL);
400 assert(strcmp(contents_3, expected) == 0);
401 free(contents_3);
402
403 // Clean up.
404 assert(unlink(filename) == 0);
405 free(filename);
406 }
407
408 static void test_read_file(void) {
409 char *const filename = get_temporary_filename();
410 assert(filename != NULL);
411
412 // Make a buffer to work with.
413 size_t const bufsz = 32;
414 char *const buf = calloc(bufsz, sizeof(char));
415 assert(buf != NULL);
416
417 // Test: The file does not exist.
418
419 memset(buf, 0, bufsz);
420 ssize_t const sz_0 = read_file(filename, buf, 2);
421 assert(sz_0 == -1);
422
423 // Test: The file is zero size.
424
425 memset(buf, 0, bufsz);
426 write_file(filename, "", 0);
427 ssize_t const sz_1 = read_file(filename, buf, 2);
428 assert(sz_1 == 0);
429
430 // Test: The file is larger than we need.
431
432 memset(buf, 0, bufsz);
433 write_file(filename, "hello", strlen("hello"));
434 ssize_t const sz_2 = read_file(filename, buf, 2);
435 assert(sz_2 == 2);
436 assert(buf[0] == 'h');
437 assert(buf[1] == 'e');
438
439 // Test: The file is exactly the size we need.
440
441 memset(buf, 0, bufsz);
442 write_file(filename, "hi", strlen("hi"));
443 ssize_t const sz_3 = read_file(filename, buf, 2);
444 assert(sz_3 == 2);
445 assert(buf[0] == 'h');
446 assert(buf[1] == 'i');
447
448 // Test: The file has data, but not as much as we ask for.
449
450 memset(buf, 0, bufsz);
451 write_file(filename, "a", strlen("a"));
452 ssize_t sz_4 = read_file(filename, buf, 2);
453 assert(sz_4 == 1);
454 assert(buf[0] == 'a');
455
456 // Clean up.
457 assert(unlink(filename) == 0);
458 free(filename);
459 free(buf);
460 }
461
462 static char *get_temporary_filename(void) {
463 size_t const sz = 64;
464
465 char *const filename = calloc(sz, sizeof(char));
466 assert(filename != NULL);
467
468 strcat(filename, "/tmp/test-file-XXXXXX");
469 int const fd = mkstemp(filename);
470 assert(fd != -1);
471
472 assert(close(fd) == 0);
473 assert(unlink(filename) == 0);
474
475 return filename;
476 }
477
478 static void write_file(char const *const path,
479 void const *const contents,
480 size_t const sz) {
481 assert(path != NULL);
482 assert(strlen(path) != 0);
483 assert(contents != NULL);
484 // Permit contents to be 0 length.
485
486 int const fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
487 if (fd == -1) {
488 fprintf(stderr, "open() failed: %s: %s\n", path, strerror(errno));
489 assert(0 == 1);
490 }
491
492 if (sz > 0) {
493 ssize_t const write_sz = write(fd, contents, sz);
494 assert(write_sz != -1);
495 assert((size_t)write_sz == sz);
496 }
497
498 assert(close(fd) == 0);
499 }
500
501 #endif