LCOV - code coverage report
Current view: top level - cmdline - import.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 93 103 90.3 %
Date: 2017-11-06 22:14:04 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2013 Andrea Mazzoleni
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "portable.h"
      19             : 
      20             : #include "support.h"
      21             : #include "import.h"
      22             : 
      23             : /****************************************************************************/
      24             : /* import */
      25             : 
      26             : /**
      27             :  * Compare the hash of two import blocks.
      28             :  */
      29        4472 : int import_block_hash_compare(const void* void_arg, const void* void_data)
      30             : {
      31        4472 :         const unsigned char* arg = void_arg;
      32        4472 :         const struct snapraid_import_block* block = void_data;
      33             : 
      34        4472 :         return memcmp(arg, block->hash, BLOCK_HASH_SIZE);
      35             : }
      36             : 
      37           0 : int import_block_prevhash_compare(const void* void_arg, const void* void_data)
      38             : {
      39           0 :         const unsigned char* arg = void_arg;
      40           0 :         const struct snapraid_import_block* block = void_data;
      41             : 
      42           0 :         return memcmp(arg, block->prevhash, BLOCK_HASH_SIZE);
      43             : }
      44             : 
      45             : /**
      46             :  * Compute the hash of the hash for an import block.
      47             :  * We just use the first 32 bit of the hash itself.
      48             :  */
      49      657477 : static inline tommy_uint32_t import_block_hash(const unsigned char* hash)
      50             : {
      51             :         /* the hash data is not aligned, and we cannot access it with a direct cast */
      52      657477 :         return hash[0] | ((uint32_t)hash[1] << 8) | ((uint32_t)hash[2] << 16) | ((uint32_t)hash[3] << 24);
      53             : }
      54             : 
      55        1750 : static void import_file(struct snapraid_state* state, const char* path, uint64_t size)
      56             : {
      57             :         struct snapraid_import_file* file;
      58             :         block_off_t i;
      59             :         data_off_t offset;
      60             :         void* buffer;
      61             :         int ret;
      62             :         int f;
      63             :         int flags;
      64        1750 :         unsigned block_size = state->block_size;
      65             :         struct advise_struct advise;
      66             : 
      67        1750 :         file = malloc_nofail(sizeof(struct snapraid_import_file));
      68        1750 :         file->path = strdup_nofail(path);
      69        1750 :         file->size = size;
      70        1750 :         file->blockmax = (size + block_size - 1) / block_size;
      71        1750 :         file->blockimp = malloc_nofail(file->blockmax * sizeof(struct snapraid_import_block));
      72             : 
      73        1750 :         buffer = malloc_nofail(block_size);
      74             : 
      75        1750 :         advise_init(&advise, state->file_mode);
      76             : 
      77             :         /* open for read */
      78        1750 :         flags = O_RDONLY | O_BINARY | advise_flags(&advise);
      79        1750 :         f = open(path, flags);
      80        1750 :         if (f == -1) {
      81             :                 /* LCOV_EXCL_START */
      82             :                 log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
      83             :                 exit(EXIT_FAILURE);
      84             :                 /* LCOV_EXCL_STOP */
      85             :         }
      86             : 
      87        1750 :         ret = advise_open(&advise, f);
      88        1750 :         if (ret != 0) {
      89             :                 /* LCOV_EXCL_START */
      90             :                 log_fatal("Error advising file '%s'. %s.\n", path, strerror(errno));
      91             :                 exit(EXIT_FAILURE);
      92             :                 /* LCOV_EXCL_STOP */
      93             :         }
      94             : 
      95        1750 :         offset = 0;
      96        6222 :         for (i = 0; i < file->blockmax; ++i) {
      97        4472 :                 struct snapraid_import_block* block = &file->blockimp[i];
      98        4472 :                 unsigned read_size = block_size;
      99        4472 :                 if (read_size > size)
     100        1745 :                         read_size = size;
     101             : 
     102        4472 :                 ret = read(f, buffer, read_size);
     103        4472 :                 if (ret < 0 || (unsigned)ret != read_size) {
     104             :                         /* LCOV_EXCL_START */
     105             :                         log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
     106             :                         exit(EXIT_FAILURE);
     107             :                         /* LCOV_EXCL_STOP */
     108             :                 }
     109             : 
     110        4472 :                 block->file = file;
     111        4472 :                 block->offset = offset;
     112        4472 :                 block->size = read_size;
     113             : 
     114        4472 :                 memhash(state->hash, state->hashseed, block->hash, buffer, read_size);
     115        4472 :                 tommy_hashdyn_insert(&state->importset, &block->nodeset, block, import_block_hash(block->hash));
     116             : 
     117             :                 /* if we are in a rehash state */
     118        4472 :                 if (state->prevhash != HASH_UNDEFINED) {
     119             :                         /* compute also the previous hash */
     120           0 :                         memhash(state->prevhash, state->prevhashseed, block->prevhash, buffer, read_size);
     121           0 :                         tommy_hashdyn_insert(&state->previmportset, &block->prevnodeset, block, import_block_hash(block->prevhash));
     122             :                 }
     123             : 
     124        4472 :                 offset += read_size;
     125        4472 :                 size -= read_size;
     126             :         }
     127             : 
     128        1750 :         ret = close(f);
     129        1750 :         if (ret != 0) {
     130             :                 /* LCOV_EXCL_START */
     131             :                 log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
     132             :                 exit(EXIT_FAILURE);
     133             :                 /* LCOV_EXCL_STOP */
     134             :         }
     135             : 
     136        1750 :         tommy_list_insert_tail(&state->importlist, &file->nodelist, file);
     137             : 
     138        1750 :         free(buffer);
     139        1750 : }
     140             : 
     141        1750 : void import_file_free(struct snapraid_import_file* file)
     142             : {
     143        1750 :         free(file->path);
     144        1750 :         free(file->blockimp);
     145        1750 :         free(file);
     146        1750 : }
     147             : 
     148      653005 : int state_import_fetch(struct snapraid_state* state, int rehash, struct snapraid_block* missing_block, unsigned char* buffer)
     149             : {
     150             :         struct snapraid_import_block* block;
     151             :         int ret;
     152             :         int f;
     153      653005 :         const unsigned char* hash = missing_block->hash;
     154      653005 :         unsigned block_size = state->block_size;
     155             :         unsigned read_size;
     156             :         unsigned char buffer_hash[HASH_MAX];
     157             :         const char* path;
     158             : 
     159      653005 :         if (rehash) {
     160           0 :                 block = tommy_hashdyn_search(&state->previmportset, import_block_prevhash_compare, hash, import_block_hash(hash));
     161             :         } else {
     162      653005 :                 block = tommy_hashdyn_search(&state->importset, import_block_hash_compare, hash, import_block_hash(hash));
     163             :         }
     164      653005 :         if (!block)
     165      648533 :                 return -1;
     166             : 
     167        4472 :         path = block->file->path;
     168        4472 :         read_size = block->size;
     169             : 
     170        4472 :         f = open(path, O_RDONLY | O_BINARY);
     171        4472 :         if (f == -1) {
     172             :                 /* LCOV_EXCL_START */
     173             :                 if (errno == ENOENT) {
     174             :                         log_fatal("DANGER! file '%s' disappeared.\n", path);
     175             :                         log_fatal("If you moved it, please rerun the same command.\n");
     176             :                 } else {
     177             :                         log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
     178             :                 }
     179             :                 exit(EXIT_FAILURE);
     180             :                 /* LCOV_EXCL_STOP */
     181             :         }
     182             : 
     183        4472 :         ret = pread(f, buffer, read_size, block->offset);
     184        4472 :         if (ret < 0 || (unsigned)ret != read_size) {
     185             :                 /* LCOV_EXCL_START */
     186             :                 log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
     187             :                 exit(EXIT_FAILURE);
     188             :                 /* LCOV_EXCL_STOP */
     189             :         }
     190             : 
     191        4472 :         ret = close(f);
     192        4472 :         if (ret != 0) {
     193             :                 /* LCOV_EXCL_START */
     194             :                 log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
     195             :                 exit(EXIT_FAILURE);
     196             :                 /* LCOV_EXCL_STOP */
     197             :         }
     198             : 
     199        4472 :         if (read_size != block_size) {
     200             :                 /* fill the remaining with 0 */
     201        1745 :                 memset(buffer + read_size, 0, block_size - read_size);
     202             :         }
     203             : 
     204             :         /* recheck the hash */
     205        4472 :         if (rehash)
     206           0 :                 memhash(state->prevhash, state->prevhashseed, buffer_hash, buffer, read_size);
     207             :         else
     208        4472 :                 memhash(state->hash, state->hashseed, buffer_hash, buffer, read_size);
     209             : 
     210        4472 :         if (memcmp(buffer_hash, hash, BLOCK_HASH_SIZE) != 0) {
     211             :                 /* LCOV_EXCL_START */
     212             :                 log_fatal("Error in data reading file '%s'.\n", path);
     213             :                 log_fatal("Please don't change imported files while running.\n");
     214             :                 exit(EXIT_FAILURE);
     215             :                 /* LCOV_EXCL_STOP */
     216             :         }
     217             : 
     218        4472 :         return 0;
     219             : }
     220             : 
     221           1 : static void import_dir(struct snapraid_state* state, const char* dir)
     222             : {
     223             :         DIR* d;
     224             : 
     225           1 :         d = opendir(dir);
     226           1 :         if (!d) {
     227             :                 /* LCOV_EXCL_START */
     228             :                 log_fatal("Error opening directory '%s'. %s.\n", dir, strerror(errno));
     229             :                 exit(EXIT_FAILURE);
     230             :                 /* LCOV_EXCL_STOP */
     231             :         }
     232             : 
     233             :         while (1) {
     234             :                 char path_next[PATH_MAX];
     235             :                 struct stat st;
     236             :                 const char* name;
     237             :                 struct dirent* dd;
     238             : 
     239             :                 /* clear errno to detect erroneous conditions */
     240        1812 :                 errno = 0;
     241        1812 :                 dd = readdir(d);
     242        1812 :                 if (dd == 0 && errno != 0) {
     243             :                         /* LCOV_EXCL_START */
     244             :                         log_fatal("Error reading directory '%s'. %s.\n", dir, strerror(errno));
     245             :                         exit(EXIT_FAILURE);
     246             :                         /* LCOV_EXCL_STOP */
     247             :                 }
     248        1812 :                 if (dd == 0) {
     249           1 :                         break; /* finished */
     250             :                 }
     251             : 
     252             :                 /* skip "." and ".." files */
     253        1811 :                 name = dd->d_name;
     254        1811 :                 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
     255           2 :                         continue;
     256             : 
     257        1809 :                 pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
     258             : 
     259             : #if HAVE_STRUCT_DIRENT_D_STAT
     260             :                 /* convert dirent to lstat result */
     261             :                 dirent_lstat(dd, &st);
     262             : 
     263             :                 /* if the st_mode field is missing, takes care to fill it using normal lstat() */
     264             :                 /* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
     265             :                 /* because we use a directory reading method that doesn't read info about ReparsePoint. */
     266             :                 /* Note that here we cannot call here lstat_sync(), because we don't know what kind */
     267             :                 /* of file is it, and lstat_sync() doesn't always work */
     268             :                 if (st.st_mode == 0) {
     269             :                         if (lstat(path_next, &st) != 0) {
     270             :                                 /* LCOV_EXCL_START */
     271             :                                 log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
     272             :                                 exit(EXIT_FAILURE);
     273             :                                 /* LCOV_EXCL_STOP */
     274             :                         }
     275             :                 }
     276             : #else
     277             :                 /* get lstat info about the file */
     278        1809 :                 if (lstat(path_next, &st) != 0) {
     279             :                         /* LCOV_EXCL_START */
     280             :                         log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
     281             :                         exit(EXIT_FAILURE);
     282             :                         /* LCOV_EXCL_STOP */
     283             :                 }
     284             : #endif
     285             : 
     286        1809 :                 if (S_ISREG(st.st_mode)) {
     287        1750 :                         import_file(state, path_next, st.st_size);
     288          59 :                 } else if (S_ISDIR(st.st_mode)) {
     289           0 :                         pathslash(path_next, sizeof(path_next));
     290           0 :                         import_dir(state, path_next);
     291             :                 }
     292        1811 :         }
     293             : 
     294           1 :         if (closedir(d) != 0) {
     295             :                 /* LCOV_EXCL_START */
     296             :                 log_fatal("Error closing directory '%s'. %s.\n", dir, strerror(errno));
     297             :                 exit(EXIT_FAILURE);
     298             :                 /* LCOV_EXCL_STOP */
     299             :         }
     300           1 : }
     301             : 
     302           1 : void state_import(struct snapraid_state* state, const char* dir)
     303             : {
     304             :         char path[PATH_MAX];
     305             : 
     306           1 :         msg_progress("Importing...\n");
     307             : 
     308             :         /* if the hash is not full */
     309           1 :         if (BLOCK_HASH_SIZE != HASH_MAX) {
     310             :                 /* LCOV_EXCL_START */
     311             :                 log_fatal("You cannot import files when using a reduced hash.\n");
     312             :                 exit(EXIT_FAILURE);
     313             :                 /* LCOV_EXCL_STOP */
     314             :         }
     315             : 
     316             :         /* add the final slash */
     317           1 :         pathimport(path, sizeof(path), dir);
     318           1 :         pathslash(path, sizeof(path));
     319             : 
     320           1 :         import_dir(state, path);
     321           1 : }
     322             : 

Generated by: LCOV version 1.13