LCOV - code coverage report
Current view: top level - cmdline - handle.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 135 139 97.1 %
Date: 2026-04-29 15:04:44 Functions: 8 8 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-3.0-or-later
       2             : // Copyright (C) 2011 Andrea Mazzoleni
       3             : 
       4             : #include "portable.h"
       5             : 
       6             : #include "elem.h"
       7             : #include "support.h"
       8             : #include "handle.h"
       9             : 
      10             : /****************************************************************************/
      11             : /* handle */
      12             : 
      13      790591 : int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, int mode)
      14             : {
      15             :         int ret;
      16             :         int flags;
      17             : 
      18             :         /* if it's the same file, and already opened, nothing to do */
      19      790591 :         if (handle->file == file && handle->f != -1) {
      20           0 :                 return 0;
      21             :         }
      22             : 
      23      790591 :         advise_init(&handle->advise, mode);
      24      790591 :         pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
      25             : 
      26      790591 :         ret = mkancestor(handle->path);
      27      790591 :         if (ret != 0) {
      28             :                 /* LCOV_EXCL_START */
      29             :                 return -1;
      30             :                 /* LCOV_EXCL_STOP */
      31             :         }
      32             : 
      33             :         /* initial values, changed later if required */
      34      790591 :         handle->created = 0;
      35             : 
      36             :         /*
      37             :          * Flags for opening
      38             :          * O_BINARY: open as binary file (Windows only)
      39             :          * O_NOFOLLOW: do not follow links to ensure to open the real file
      40             :          */
      41      790591 :         flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
      42             : 
      43             :         /* open for read write */
      44      790591 :         handle->f = open(handle->path, flags | O_RDWR);
      45             : 
      46             :         /* if failed for missing write permission */
      47      790591 :         if (handle->f == -1 && (errno == EACCES || errno == EROFS)) {
      48             :                 /* open for read-only */
      49           0 :                 handle->f = open(handle->path, flags | O_RDONLY);
      50             :         }
      51             : 
      52             :         /* if failed for missing file */
      53      790591 :         if (handle->f == -1 && errno == ENOENT) {
      54             :                 char path_from[PATH_MAX];
      55             : 
      56             :                 /* check if exists a .unrecoverable copy, and rename to the real one */
      57      186769 :                 pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
      58             : 
      59      186769 :                 if (rename(path_from, handle->path) == 0) {
      60             :                         /* open for read write */
      61       62986 :                         handle->f = open(handle->path, flags | O_RDWR);
      62             :                 } else {
      63             :                         /* create it */
      64      123783 :                         handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
      65      123783 :                         if (handle->f != -1) {
      66             :                                 /* mark it as created if really done */
      67      123783 :                                 handle->created = 1;
      68             :                         }
      69             :                 }
      70             :         }
      71             : 
      72      790591 :         if (handle->f == -1) {
      73             :                 /* LCOV_EXCL_START */
      74             :                 log_fatal(errno, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
      75             :                 handle_close(handle);
      76             :                 return -1;
      77             :                 /* LCOV_EXCL_STOP */
      78             :         }
      79             : 
      80             :         /* just opened */
      81      790591 :         handle->file = file;
      82             : 
      83             :         /* get the stat info */
      84      790591 :         ret = fstat(handle->f, &handle->st);
      85      790591 :         if (ret != 0) {
      86             :                 /* LCOV_EXCL_START */
      87             :                 log_fatal(errno, "Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
      88             :                 handle_close(handle);
      89             :                 return -1;
      90             :                 /* LCOV_EXCL_STOP */
      91             :         }
      92             : 
      93             :         /* get the size of the existing data */
      94      790591 :         handle->valid_size = handle->st.st_size;
      95             : 
      96      790591 :         ret = advise_open(&handle->advise, handle->f);
      97      790591 :         if (ret != 0) {
      98             :                 /* LCOV_EXCL_START */
      99             :                 log_fatal(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     100             :                 handle_close(handle);
     101             :                 return -1;
     102             :                 /* LCOV_EXCL_STOP */
     103             :         }
     104             : 
     105      790591 :         return 0;
     106             : }
     107             : 
     108        3131 : int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
     109             : {
     110             :         int ret;
     111             : 
     112        3131 :         ret = ftruncate(handle->f, file->size);
     113        3131 :         if (ret != 0) {
     114             :                 /* LCOV_EXCL_START */
     115             :                 if (errno == EACCES) {
     116             :                         log_fatal(errno, "Failed to truncate file '%s' for missing write permission.\n", handle->path);
     117             :                 } else {
     118             :                         log_fatal(errno, "Error truncating file '%s'. %s.\n", handle->path, strerror(errno));
     119             :                 }
     120             :                 return -1;
     121             :                 /* LCOV_EXCL_STOP */
     122             :         }
     123             : 
     124             :         /* adjust the size to the truncated size */
     125        3131 :         handle->valid_size = file->size;
     126             : 
     127        3131 :         return 0;
     128             : }
     129             : 
     130     2327527 : int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, log_ptr* out, log_ptr* out_missing)
     131             : {
     132             :         int ret;
     133             :         int flags;
     134             : 
     135     2327527 :         if (!out_missing)
     136     1652166 :                 out_missing = out;
     137             : 
     138             :         /* if already opened, nothing to do */
     139     2327527 :         if (handle->file == file && handle->file != 0 && handle->f != -1) {
     140      498867 :                 return 0;
     141             :         }
     142             : 
     143     1828660 :         advise_init(&handle->advise, mode);
     144     1828660 :         pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
     145             : 
     146             :         /* for sure not created */
     147     1828660 :         handle->created = 0;
     148             : 
     149             :         /*
     150             :          * Flags for opening
     151             :          * O_BINARY: open as binary file (Windows only)
     152             :          * O_NOFOLLOW: do not follow links to ensure to open the real file
     153             :          */
     154     1828660 :         flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
     155             : 
     156             :         /* open for read */
     157     1828660 :         handle->f = open_noatime(handle->path, flags | O_RDONLY);
     158     1828660 :         if (handle->f == -1) {
     159       86637 :                 if (errno == ENOENT)
     160       86625 :                         out_missing(errno, "Missing file '%s'.\n", handle->path);
     161          12 :                 else if (errno == EACCES)
     162          12 :                         out(errno, "Permission denied for file '%s'.\n", handle->path);
     163             :                 else
     164           0 :                         out(errno, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
     165       86637 :                 handle_close(handle);
     166       86637 :                 return -1;
     167             :         }
     168             : 
     169             :         /* just opened */
     170     1742023 :         handle->file = file;
     171             : 
     172             :         /* get the stat info */
     173     1742023 :         ret = fstat(handle->f, &handle->st);
     174     1742023 :         if (ret != 0) {
     175             :                 /* LCOV_EXCL_START */
     176             :                 out(errno, "Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
     177             :                 handle_close(handle);
     178             :                 return -1;
     179             :                 /* LCOV_EXCL_STOP */
     180             :         }
     181             : 
     182             :         /* get the size of the existing data */
     183     1742023 :         handle->valid_size = handle->st.st_size;
     184             : 
     185     1742023 :         ret = advise_open(&handle->advise, handle->f);
     186     1742023 :         if (ret != 0) {
     187             :                 /* LCOV_EXCL_START */
     188             :                 out(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     189             :                 handle_close(handle);
     190             :                 return -1;
     191             :                 /* LCOV_EXCL_STOP */
     192             :         }
     193             : 
     194     1742023 :         return 0;
     195             : }
     196             : 
     197     4967235 : int handle_close(struct snapraid_handle* handle)
     198             : {
     199             :         int ret;
     200             : 
     201             :         /* close if open */
     202     4967235 :         if (handle->f != -1) {
     203     2532561 :                 ret = close(handle->f);
     204     2532561 :                 if (ret != 0) {
     205             :                         /* LCOV_EXCL_START */
     206             :                         log_fatal(errno, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
     207             : 
     208             :                         /* invalidate for error */
     209             :                         handle->file = 0;
     210             :                         handle->f = -1;
     211             :                         handle->valid_size = 0;
     212             :                         return -1;
     213             :                         /* LCOV_EXCL_STOP */
     214             :                 }
     215             :         }
     216             : 
     217             :         /* reset the descriptor */
     218     4967235 :         handle->file = 0;
     219     4967235 :         handle->f = -1;
     220     4967235 :         handle->valid_size = 0;
     221             : 
     222     4967235 :         return 0;
     223             : }
     224             : 
     225     5835406 : int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, log_ptr* out, log_ptr* out_missing)
     226             : {
     227             :         ssize_t read_ret;
     228             :         data_off_t offset;
     229             :         unsigned read_size;
     230             :         unsigned count;
     231             :         int ret;
     232             : 
     233     5835406 :         offset = file_pos * (data_off_t)block_size;
     234             : 
     235     5835406 :         if (!out_missing)
     236     5828823 :                 out_missing = out;
     237             : 
     238             :         /* check if we are going to read only not initialized data */
     239     5835406 :         if (offset >= handle->valid_size) {
     240             :                 /* if the file is missing, it's at 0 size, or it's rebuilt while reading */
     241      187110 :                 if (offset == handle->valid_size || handle->valid_size == 0) {
     242      186084 :                         errno = ENOENT;
     243      186084 :                         if (offset == 0) {
     244      185144 :                                 out_missing(errno, "Missing file '%s'.\n", handle->path);
     245             :                         } else {
     246         940 :                                 out_missing(errno, "Missing data in file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
     247             :                         }
     248             :                 } else {
     249        1026 :                         errno = ENXIO;
     250        1026 :                         out(errno, "Reading over the end from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
     251             :                 }
     252      187110 :                 return -1;
     253             :         }
     254             : 
     255     5648296 :         read_size = file_block_size(handle->file, file_pos, block_size);
     256             : 
     257     5648296 :         count = 0;
     258     5648296 :         errno = 0;
     259             :         do {
     260     5657114 :                 bw_limit(handle->bw, block_size - count);
     261             : 
     262     5657114 :                 read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
     263     5657114 :                 if (read_ret == -1) {
     264             :                         /* LCOV_EXCL_START */
     265             :                         out(errno, "Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", handle->path, offset + count, block_size - count, strerror(errno));
     266             :                         return -1;
     267             :                         /* LCOV_EXCL_STOP */
     268             :                 }
     269     5657114 :                 if (read_ret == 0) {
     270             :                         /* LCOV_EXCL_START */
     271             :                         if (errno == 0)
     272             :                                 errno = ENXIO;
     273             :                         out(errno, "Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
     274             :                         return -1;
     275             :                         /* LCOV_EXCL_STOP */
     276             :                 }
     277             : 
     278     5648296 :                 count += read_ret;
     279     5648296 :         } while (count < read_size);
     280             : 
     281             :         /* pad with 0 */
     282     5639478 :         if (read_size < block_size) {
     283     2302942 :                 memset(block_buffer + read_size, 0, block_size - read_size);
     284             :         }
     285             : 
     286     5639478 :         ret = advise_read(&handle->advise, handle->f, offset, block_size);
     287     5639478 :         if (ret != 0) {
     288             :                 /* LCOV_EXCL_START */
     289             :                 out(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     290             :                 return -1;
     291             :                 /* LCOV_EXCL_STOP */
     292             :         }
     293             : 
     294     5639478 :         return read_size;
     295             : }
     296             : 
     297      310483 : int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size)
     298             : {
     299             :         ssize_t write_ret;
     300             :         data_off_t offset;
     301             :         unsigned write_size;
     302             :         unsigned count;
     303             :         int ret;
     304             : 
     305      310483 :         offset = file_pos * (data_off_t)block_size;
     306             : 
     307      310483 :         write_size = file_block_size(handle->file, file_pos, block_size);
     308             : 
     309      310483 :         count = 0;
     310             :         do {
     311      310483 :                 bw_limit(handle->bw, write_size - count);
     312             : 
     313      310483 :                 write_ret = pwrite(handle->f, block_buffer + count, write_size - count, offset + count);
     314      310483 :                 if (write_ret == -1) {
     315             :                         /* LCOV_EXCL_START */
     316             :                         log_fatal(errno, "Error writing file '%s'. %s.\n", handle->path, strerror(errno));
     317             :                         return -1;
     318             :                         /* LCOV_EXCL_STOP */
     319             :                 }
     320      310483 :                 if (write_ret == 0) {
     321             :                         /* LCOV_EXCL_START */
     322             :                         errno = ENXIO;
     323             :                         log_fatal(errno, "Unexpected 0 write to file '%s'. %s.\n", handle->path, strerror(errno));
     324             :                         return -1;
     325             :                         /* LCOV_EXCL_STOP */
     326             :                 }
     327             : 
     328      310483 :                 count += write_ret;
     329      310483 :         } while (count < write_size);
     330             : 
     331             :         /* adjust the size of the valid data */
     332      310483 :         if (handle->valid_size < offset + write_size) {
     333      304285 :                 handle->valid_size = offset + write_size;
     334             :         }
     335             : 
     336      310483 :         ret = advise_write(&handle->advise, handle->f, offset, block_size);
     337      310483 :         if (ret != 0) {
     338             :                 /* LCOV_EXCL_START */
     339             :                 log_fatal(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     340             :                 return -1;
     341             :                 /* LCOV_EXCL_STOP */
     342             :         }
     343             : 
     344      310483 :         return 0;
     345             : }
     346             : 
     347      131956 : int handle_utime(struct snapraid_handle* handle)
     348             : {
     349             :         int ret;
     350             : 
     351             :         /* do nothing if not opened */
     352      131956 :         if (handle->f == -1)
     353           0 :                 return 0;
     354             : 
     355      131956 :         ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
     356             : 
     357      131956 :         if (ret != 0) {
     358             :                 /* LCOV_EXCL_START */
     359             :                 log_fatal(errno, "Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno));
     360             :                 return -1;
     361             :                 /* LCOV_EXCL_STOP */
     362             :         }
     363             : 
     364      131956 :         return 0;
     365             : }
     366             : 
     367         254 : struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
     368             : {
     369             :         tommy_node* i;
     370             :         unsigned j;
     371         254 :         unsigned size = 0;
     372             :         struct snapraid_handle* handle;
     373             : 
     374             :         /* get the size of the mapping */
     375         254 :         size = 0;
     376        1716 :         for (i = state->maplist; i != 0; i = i->next) {
     377        1462 :                 struct snapraid_map* map = i->data;
     378        1462 :                 if (map->position > size)
     379        1043 :                         size = map->position;
     380             :         }
     381         254 :         ++size; /* size is one more than the max */
     382             : 
     383         254 :         handle = malloc_nofail(size * sizeof(struct snapraid_handle));
     384             : 
     385        1721 :         for (j = 0; j < size; ++j) {
     386             :                 /* default for empty position */
     387        1467 :                 handle[j].disk = 0;
     388        1467 :                 handle[j].file = 0;
     389        1467 :                 handle[j].f = -1;
     390        1467 :                 handle[j].valid_size = 0;
     391        1467 :                 handle[j].bw = 0;
     392             :         }
     393             : 
     394             :         /* set the vector */
     395        1716 :         for (i = state->disklist; i != 0; i = i->next) {
     396             :                 struct snapraid_map* map;
     397        1462 :                 struct snapraid_disk* disk = i->data;
     398             :                 tommy_node* k;
     399             : 
     400             :                 /* search the mapping for this disk */
     401        1462 :                 map = 0;
     402        5019 :                 for (k = state->maplist; k != 0; k = k->next) {
     403        5019 :                         map = k->data;
     404        5019 :                         if (strcmp(disk->name, map->name) == 0)
     405        1462 :                                 break;
     406             :                 }
     407        1462 :                 if (!map) {
     408             :                         /* LCOV_EXCL_START */
     409             :                         log_fatal(EINTERNAL, "Internal error for inconsistent disk mapping.\n");
     410             :                         os_abort();
     411             :                         /* LCOV_EXCL_STOP */
     412             :                 }
     413             : 
     414        1462 :                 handle[map->position].disk = disk;
     415             :         }
     416             : 
     417         254 :         *handlemax = size;
     418         254 :         return handle;
     419             : }
     420             : 

Generated by: LCOV version 1.0