LCOV - code coverage report
Current view: top level - cmdline - handle.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 137 141 97.2 %
Date: 2026-03-15 15:58:19 Functions: 8 8 100.0 %

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

Generated by: LCOV version 1.0