LCOV - code coverage report
Current view: top level - cmdline - search.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 91 99 91.9 %
Date: 2026-04-29 15:04:44 Functions: 7 7 100.0 %

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

Generated by: LCOV version 1.0