LCOV - code coverage report
Current view: top level - cmdline - search.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 90 99 90.9 %
Date: 2017-11-06 22:14:04 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2014 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 "search.h"
      22             : 
      23             : /****************************************************************************/
      24             : /* search */
      25             : 
      26     1843562 : static void search_file(struct snapraid_state* state, const char* path, data_off_t size, int64_t mtime_sec, int mtime_nsec)
      27             : {
      28             :         struct snapraid_search_file* file;
      29             :         tommy_uint32_t file_hash;
      30             : 
      31     1843562 :         file = malloc_nofail(sizeof(struct snapraid_search_file));
      32     1843562 :         file->path = strdup_nofail(path);
      33     1843562 :         file->size = size;
      34     1843562 :         file->mtime_sec = mtime_sec;
      35     1843562 :         file->mtime_nsec = mtime_nsec;
      36             : 
      37     1843562 :         file_hash = file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec);
      38             : 
      39     1843562 :         tommy_hashdyn_insert(&state->searchset, &file->node, file, file_hash);
      40     1843562 : }
      41             : 
      42     1843562 : void search_file_free(struct snapraid_search_file* file)
      43             : {
      44     1843562 :         free(file->path);
      45     1843562 :         free(file);
      46     1843562 : }
      47             : 
      48             : struct search_file_compare_arg {
      49             :         const struct snapraid_state* state;
      50             :         const struct snapraid_block* block;
      51             :         const struct snapraid_file* file;
      52             :         unsigned char* buffer;
      53             :         data_off_t offset;
      54             :         unsigned read_size;
      55             :         int prevhash;
      56             : };
      57             : 
      58       20151 : int search_file_compare(const void* void_arg, const void* void_data)
      59             : {
      60       20151 :         const struct search_file_compare_arg* arg = void_arg;
      61       20151 :         const struct snapraid_search_file* file = void_data;
      62       20151 :         const struct snapraid_state* state = arg->state;
      63             :         unsigned char buffer_hash[HASH_MAX];
      64       20151 :         const char* path = file->path;
      65             :         int f;
      66             :         ssize_t ret;
      67             : 
      68             :         /* compare file info */
      69       20151 :         if (arg->file->size != file->size)
      70           0 :                 return -1;
      71             : 
      72       20151 :         if (arg->file->mtime_sec != file->mtime_sec)
      73           0 :                 return -1;
      74             : 
      75       20151 :         if (arg->file->mtime_nsec != file->mtime_nsec)
      76           0 :                 return -1;
      77             : 
      78             :         /* read the block and compare the hash */
      79       20151 :         f = open(path, O_RDONLY | O_BINARY);
      80       20151 :         if (f == -1) {
      81             :                 /* LCOV_EXCL_START */
      82             :                 if (errno == ENOENT) {
      83             :                         log_fatal("DANGER! file '%s' disappeared.\n", path);
      84             :                         log_fatal("If you moved it, please rerun the same command.\n");
      85             :                 } else {
      86             :                         log_fatal("Error opening file '%s'. %s.\n", path, strerror(errno));
      87             :                 }
      88             :                 exit(EXIT_FAILURE);
      89             :                 /* LCOV_EXCL_STOP */
      90             :         }
      91             : 
      92       20151 :         ret = pread(f, arg->buffer, arg->read_size, arg->offset);
      93       20151 :         if (ret < 0 || (unsigned)ret != arg->read_size) {
      94             :                 /* LCOV_EXCL_START */
      95             :                 log_fatal("Error reading file '%s'. %s.\n", path, strerror(errno));
      96             :                 exit(EXIT_FAILURE);
      97             :                 /* LCOV_EXCL_STOP */
      98             :         }
      99             : 
     100       20151 :         ret = close(f);
     101       20151 :         if (ret != 0) {
     102             :                 /* LCOV_EXCL_START */
     103             :                 log_fatal("Error closing file '%s'. %s.\n", path, strerror(errno));
     104             :                 exit(EXIT_FAILURE);
     105             :                 /* LCOV_EXCL_STOP */
     106             :         }
     107             : 
     108             :         /* compute the hash */
     109       20151 :         if (arg->prevhash)
     110           0 :                 memhash(state->prevhash, state->prevhashseed, buffer_hash, arg->buffer, arg->read_size);
     111             :         else
     112       20151 :                 memhash(state->hash, state->hashseed, buffer_hash, arg->buffer, arg->read_size);
     113             : 
     114             :         /* check if the hash is matching */
     115       20151 :         if (memcmp(buffer_hash, arg->block->hash, BLOCK_HASH_SIZE) != 0)
     116        2241 :                 return -1;
     117             : 
     118       17910 :         if (arg->read_size != state->block_size) {
     119             :                 /* fill the remaining with 0 */
     120        7108 :                 memset(arg->buffer + arg->read_size, 0, state->block_size - arg->read_size);
     121             :         }
     122             : 
     123       17910 :         return 0;
     124             : }
     125             : 
     126      639489 : int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_file* missing_file, block_off_t missing_file_pos, struct snapraid_block* missing_block, unsigned char* buffer)
     127             : {
     128             :         struct snapraid_search_file* file;
     129             :         tommy_uint32_t file_hash;
     130             :         struct search_file_compare_arg arg;
     131             : 
     132      639489 :         arg.state = state;
     133      639489 :         arg.block = missing_block;
     134      639489 :         arg.file = missing_file;
     135      639489 :         arg.buffer = buffer;
     136      639489 :         arg.offset = state->block_size * (data_off_t)missing_file_pos;
     137      639489 :         arg.read_size = file_block_size(missing_file, missing_file_pos, state->block_size);
     138      639489 :         arg.prevhash = prevhash;
     139             : 
     140      639489 :         file_hash = file_stamp_hash(arg.file->size, arg.file->mtime_sec, arg.file->mtime_nsec);
     141             : 
     142             :         /* search in the hashtable, and also check if the data matches the hash */
     143      639489 :         file = tommy_hashdyn_search(&state->searchset, search_file_compare, &arg, file_hash);
     144      639489 :         if (!file)
     145      621579 :                 return -1;
     146             : 
     147             :         /* if found, buffer is already set with data */
     148       17910 :         return 0;
     149             : }
     150             : 
     151        2462 : static void search_dir(struct snapraid_state* state, struct snapraid_disk* disk, const char* dir, const char* sub)
     152             : {
     153             :         DIR* d;
     154             : 
     155        2462 :         d = opendir(dir);
     156        2462 :         if (!d) {
     157             :                 /* LCOV_EXCL_START */
     158             :                 log_fatal("Error opening directory '%s'. %s.\n", dir, strerror(errno));
     159             :                 exit(EXIT_FAILURE);
     160             :                 /* LCOV_EXCL_STOP */
     161             :         }
     162             : 
     163             :         while (1) {
     164             :                 char path_next[PATH_MAX];
     165             :                 char sub_next[PATH_MAX];
     166             :                 char out[PATH_MAX];
     167     2040010 :                 struct snapraid_filter* reason = 0;
     168             :                 struct stat st;
     169             :                 const char* name;
     170             :                 struct dirent* dd;
     171             : 
     172             :                 /* clear errno to detect erroneous conditions */
     173     2040010 :                 errno = 0;
     174     2040010 :                 dd = readdir(d);
     175     2040010 :                 if (dd == 0 && errno != 0) {
     176             :                         /* LCOV_EXCL_START */
     177             :                         log_fatal("Error reading directory '%s'. %s.\n", dir, strerror(errno));
     178             :                         exit(EXIT_FAILURE);
     179             :                         /* LCOV_EXCL_STOP */
     180             :                 }
     181     2040010 :                 if (dd == 0) {
     182        2462 :                         break; /* finished */
     183             :                 }
     184             : 
     185             :                 /* skip "." and ".." files */
     186     2037548 :                 name = dd->d_name;
     187     2037548 :                 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
     188        9848 :                         continue;
     189             : 
     190     2032624 :                 pathprint(path_next, sizeof(path_next), "%s%s", dir, name);
     191     2032624 :                 pathprint(sub_next, sizeof(sub_next), "%s%s", sub, name);
     192             : 
     193             :                 /* exclude hidden files even before calling lstat() */
     194     2032624 :                 if (disk != 0 && filter_hidden(state->filter_hidden, dd) != 0) {
     195           0 :                         msg_verbose("Excluding hidden '%s'\n", path_next);
     196           0 :                         continue;
     197             :                 }
     198             : 
     199             :                 /* exclude content files even before calling lstat() */
     200     2032624 :                 if (disk != 0 && filter_content(&state->contentlist, path_next) != 0) {
     201           0 :                         msg_verbose("Excluding content '%s'\n", path_next);
     202           0 :                         continue;
     203             :                 }
     204             : 
     205             : #if HAVE_STRUCT_DIRENT_D_STAT
     206             :                 /* convert dirent to lstat result */
     207             :                 dirent_lstat(dd, &st);
     208             : 
     209             :                 /* if the st_mode field is missing, takes care to fill it using normal lstat() */
     210             :                 /* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
     211             :                 /* because we use a directory reading method that doesn't read info about ReparsePoint. */
     212             :                 /* Note that here we cannot call here lstat_sync(), because we don't know what kind */
     213             :                 /* of file is it, and lstat_sync() doesn't always work */
     214             :                 if (st.st_mode == 0) {
     215             :                         if (lstat(path_next, &st) != 0) {
     216             :                                 /* LCOV_EXCL_START */
     217             :                                 log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
     218             :                                 exit(EXIT_FAILURE);
     219             :                                 /* LCOV_EXCL_STOP */
     220             :                         }
     221             :                 }
     222             : #else
     223             :                 /* get lstat info about the file */
     224     2032624 :                 if (lstat(path_next, &st) != 0) {
     225             :                         /* LCOV_EXCL_START */
     226             :                         log_fatal("Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
     227             :                         exit(EXIT_FAILURE);
     228             :                         /* LCOV_EXCL_STOP */
     229             :                 }
     230             : #endif
     231             : 
     232     2032624 :                 if (S_ISREG(st.st_mode)) {
     233     1968347 :                         if (disk == 0 || filter_path(&state->filterlist, &reason, disk->name, sub_next) == 0) {
     234     1843562 :                                 search_file(state, path_next, st.st_size, st.st_mtime, STAT_NSEC(&st));
     235             :                         } else {
     236      124785 :                                 msg_verbose("Excluding link '%s' for rule '%s'\n", path_next, filter_type(reason, out, sizeof(out)));
     237             :                         }
     238       64277 :                 } else if (S_ISDIR(st.st_mode)) {
     239        1781 :                         if (disk == 0 || filter_subdir(&state->filterlist, &reason, disk->name, sub_next) == 0) {
     240        1781 :                                 pathslash(path_next, sizeof(path_next));
     241        1781 :                                 pathslash(sub_next, sizeof(sub_next));
     242        1781 :                                 search_dir(state, disk, path_next, sub_next);
     243             :                         } else {
     244           0 :                                 msg_verbose("Excluding directory '%s' for rule '%s'\n", path_next, filter_type(reason, out, sizeof(out)));
     245             :                         }
     246             :                 }
     247     2037548 :         }
     248             : 
     249        2462 :         if (closedir(d) != 0) {
     250             :                 /* LCOV_EXCL_START */
     251             :                 log_fatal("Error closing directory '%s'. %s.\n", dir, strerror(errno));
     252             :                 exit(EXIT_FAILURE);
     253             :                 /* LCOV_EXCL_STOP */
     254             :         }
     255        2462 : }
     256             : 
     257           1 : void state_search(struct snapraid_state* state, const char* dir)
     258             : {
     259             :         char path[PATH_MAX];
     260             : 
     261           1 :         msg_progress("Importing...\n");
     262             : 
     263             :         /* add the final slash */
     264           1 :         pathimport(path, sizeof(path), dir);
     265           1 :         pathslash(path, sizeof(path));
     266             : 
     267           1 :         search_dir(state, 0, path, "");
     268           1 : }
     269             : 
     270         114 : void state_search_array(struct snapraid_state* state)
     271             : {
     272             :         tommy_node* i;
     273             : 
     274             :         /* import from all the disks */
     275         795 :         for (i = state->disklist; i != 0; i = i->next) {
     276         681 :                 struct snapraid_disk* disk = i->data;
     277             : 
     278             :                 /* skip data disks that are not accessible */
     279         681 :                 if (disk->skip_access)
     280           1 :                         continue;
     281             : 
     282         680 :                 msg_progress("Searching disk %s...\n", disk->name);
     283             : 
     284         680 :                 search_dir(state, disk, disk->dir, "");
     285             :         }
     286         114 : }
     287             : 

Generated by: LCOV version 1.13