"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