LCOV - code coverage report
Current view: top level - cmdline - handle.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 128 131 97.7 %
Date: 2025-10-28 11:59:11 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      771326 : 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      771326 :         if (handle->file == file && handle->f != -1) {
      34           0 :                 return 0;
      35             :         }
      36             : 
      37      771326 :         advise_init(&handle->advise, mode);
      38      771326 :         pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
      39             : 
      40      771326 :         ret = mkancestor(handle->path);
      41      771326 :         if (ret != 0) {
      42             :                 /* LCOV_EXCL_START */
      43             :                 return -1;
      44             :                 /* LCOV_EXCL_STOP */
      45             :         }
      46             : 
      47             :         /* initial values, changed later if required */
      48      771326 :         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      771326 :         flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
      54             : 
      55             :         /* open for read write */
      56      771326 :         handle->f = open(handle->path, flags | O_RDWR);
      57             : 
      58             :         /* if failed for missing write permission */
      59      771326 :         if (handle->f == -1 && (errno == EACCES || errno == EROFS)) {
      60             :                 /* open for real-only */
      61           0 :                 handle->f = open(handle->path, flags | O_RDONLY);
      62             :         }
      63             : 
      64             :         /* if failed for missing file */
      65      771326 :         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      176347 :                 pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
      70             : 
      71      176347 :                 if (rename(path_from, handle->path) == 0) {
      72             :                         /* open for read write */
      73       63292 :                         handle->f = open(handle->path, flags | O_RDWR);
      74             :                 } else {
      75             :                         /* create it */
      76      113055 :                         handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
      77      113055 :                         if (handle->f != -1) {
      78             :                                 /* mark it as created if really done */
      79      113055 :                                 handle->created = 1;
      80             :                         }
      81             :                 }
      82             :         }
      83             : 
      84      771326 :         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("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
      92             :                 return -1;
      93             :                 /* LCOV_EXCL_STOP */
      94             :         }
      95             : 
      96             :         /* just opened */
      97      771326 :         handle->file = file;
      98             : 
      99             :         /* get the stat info */
     100      771326 :         ret = fstat(handle->f, &handle->st);
     101      771326 :         if (ret != 0) {
     102             :                 /* LCOV_EXCL_START */
     103             :                 log_fatal("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      771326 :         handle->valid_size = handle->st.st_size;
     110             : 
     111      771326 :         ret = advise_open(&handle->advise, handle->f);
     112      771326 :         if (ret != 0) {
     113             :                 /* LCOV_EXCL_START */
     114             :                 log_fatal("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     115             :                 return -1;
     116             :                 /* LCOV_EXCL_STOP */
     117             :         }
     118             : 
     119      771326 :         return 0;
     120             : }
     121             : 
     122        3197 : int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
     123             : {
     124             :         int ret;
     125             : 
     126        3197 :         ret = ftruncate(handle->f, file->size);
     127        3197 :         if (ret != 0) {
     128             :                 /* LCOV_EXCL_START */
     129             :                 if (errno == EACCES) {
     130             :                         log_fatal("Failed to truncate file '%s' for missing write permission.\n", handle->path);
     131             :                 } else {
     132             :                         log_fatal("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        3197 :         handle->valid_size = file->size;
     140             : 
     141        3197 :         return 0;
     142             : }
     143             : 
     144     2175789 : int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, fptr* out, fptr* out_missing)
     145             : {
     146             :         int ret;
     147             :         int flags;
     148             : 
     149     2175789 :         if (!out_missing)
     150     2175293 :                 out_missing = out;
     151             : 
     152             :         /* if already opened, nothing to do */
     153     2175789 :         if (handle->file == file && handle->file != 0 && handle->f != -1) {
     154      460789 :                 return 0;
     155             :         }
     156             : 
     157     1715000 :         advise_init(&handle->advise, mode);
     158     1715000 :         pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
     159             : 
     160             :         /* for sure not created */
     161     1715000 :         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     1715000 :         flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
     167             : 
     168             :         /* open for read */
     169     1715000 :         handle->f = open_noatime(handle->path, flags | O_RDONLY);
     170     1715000 :         if (handle->f == -1) {
     171             :                 /* invalidate for error */
     172       78656 :                 handle->file = 0;
     173       78656 :                 handle->f = -1;
     174       78656 :                 handle->valid_size = 0;
     175             : 
     176       78656 :                 if (errno == ENOENT)
     177       78644 :                         out_missing("Missing file '%s'.\n", handle->path);
     178             :                 else
     179          12 :                         out("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
     180       78656 :                 return -1;
     181             :         }
     182             : 
     183             :         /* just opened */
     184     1636344 :         handle->file = file;
     185             : 
     186             :         /* get the stat info */
     187     1636344 :         ret = fstat(handle->f, &handle->st);
     188     1636344 :         if (ret != 0) {
     189             :                 /* LCOV_EXCL_START */
     190             :                 out("Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
     191             :                 return -1;
     192             :                 /* LCOV_EXCL_STOP */
     193             :         }
     194             : 
     195             :         /* get the size of the existing data */
     196     1636344 :         handle->valid_size = handle->st.st_size;
     197             : 
     198     1636344 :         ret = advise_open(&handle->advise, handle->f);
     199     1636344 :         if (ret != 0) {
     200             :                 /* LCOV_EXCL_START */
     201             :                 out("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     202             :                 return -1;
     203             :                 /* LCOV_EXCL_STOP */
     204             :         }
     205             : 
     206     1636344 :         return 0;
     207             : }
     208             : 
     209     4655251 : int handle_close(struct snapraid_handle* handle)
     210             : {
     211             :         int ret;
     212             : 
     213             :         /* close if open */
     214     4655251 :         if (handle->f != -1) {
     215     2407670 :                 ret = close(handle->f);
     216     2407670 :                 if (ret != 0) {
     217             :                         /* LCOV_EXCL_START */
     218             :                         log_fatal("Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
     219             : 
     220             :                         /* invalidate for error */
     221             :                         handle->file = 0;
     222             :                         handle->f = -1;
     223             :                         handle->valid_size = 0;
     224             :                         return -1;
     225             :                         /* LCOV_EXCL_STOP */
     226             :                 }
     227             :         }
     228             : 
     229             :         /* reset the descriptor */
     230     4655251 :         handle->file = 0;
     231     4655251 :         handle->f = -1;
     232     4655251 :         handle->valid_size = 0;
     233             : 
     234     4655251 :         return 0;
     235             : }
     236             : 
     237     5859021 : int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing)
     238             : {
     239             :         ssize_t read_ret;
     240             :         data_off_t offset;
     241             :         unsigned read_size;
     242             :         unsigned count;
     243             :         int ret;
     244             : 
     245     5859021 :         offset = file_pos * (data_off_t)block_size;
     246             : 
     247     5859021 :         if (!out_missing)
     248     5846130 :                 out_missing = out;
     249             : 
     250             :         /* check if we are going to read only not initialized data */
     251     5859021 :         if (offset >= handle->valid_size) {
     252             :                 /* if the file is missing, it's at 0 size, or it's rebuilt while reading */
     253      433267 :                 if (offset == handle->valid_size || handle->valid_size == 0)
     254      431972 :                         out_missing("Reading data from missing file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
     255             :                 else
     256        1295 :                         out("Reading missing data from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
     257      433267 :                 return -1;
     258             :         }
     259             : 
     260     5425754 :         read_size = file_block_size(handle->file, file_pos, block_size);
     261             : 
     262     5425754 :         count = 0;
     263             :         do {
     264     5434662 :                 bw_limit(handle->bw, block_size - count);
     265             : 
     266             :                 /* read the full block to support O_DIRECT */
     267     5434662 :                 read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
     268     5434662 :                 if (read_ret < 0) {
     269             :                         /* LCOV_EXCL_START */
     270             :                         out("Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", handle->path, offset + count, block_size - count, strerror(errno));
     271             :                         return -1;
     272             :                         /* LCOV_EXCL_STOP */
     273             :                 }
     274     5434662 :                 if (read_ret == 0) {
     275        8908 :                         out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
     276        8908 :                         return -1;
     277             :                 }
     278             : 
     279     5425754 :                 count += read_ret;
     280     5425754 :         } while (count < read_size);
     281             : 
     282             :         /* pad with 0 */
     283     5416846 :         if (read_size < block_size) {
     284     2189817 :                 memset(block_buffer + read_size, 0, block_size - read_size);
     285             :         }
     286             : 
     287     5416846 :         ret = advise_read(&handle->advise, handle->f, offset, block_size);
     288     5416846 :         if (ret != 0) {
     289             :                 /* LCOV_EXCL_START */
     290             :                 out("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     291             :                 return -1;
     292             :                 /* LCOV_EXCL_STOP */
     293             :         }
     294             : 
     295     5416846 :         return read_size;
     296             : }
     297             : 
     298      288437 : int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size)
     299             : {
     300             :         ssize_t write_ret;
     301             :         data_off_t offset;
     302             :         unsigned write_size;
     303             :         int ret;
     304             : 
     305      288437 :         offset = file_pos * (data_off_t)block_size;
     306             : 
     307      288437 :         write_size = file_block_size(handle->file, file_pos, block_size);
     308             : 
     309      288437 :         write_ret = pwrite(handle->f, block_buffer, write_size, offset);
     310      288437 :         if (write_ret != (ssize_t)write_size) { /* conversion is safe because block_size is always small */
     311             :                 /* LCOV_EXCL_START */
     312             :                 log_fatal("Error writing file '%s'. %s.\n", handle->path, strerror(errno));
     313             :                 return -1;
     314             :                 /* LCOV_EXCL_STOP */
     315             :         }
     316             : 
     317             :         /* adjust the size of the valid data */
     318      288437 :         if (handle->valid_size < offset + write_size) {
     319      282354 :                 handle->valid_size = offset + write_size;
     320             :         }
     321             : 
     322      288437 :         ret = advise_write(&handle->advise, handle->f, offset, block_size);
     323      288437 :         if (ret != 0) {
     324             :                 /* LCOV_EXCL_START */
     325             :                 log_fatal("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
     326             :                 return -1;
     327             :                 /* LCOV_EXCL_STOP */
     328             :         }
     329             : 
     330      288437 :         return 0;
     331             : }
     332             : 
     333      121230 : int handle_utime(struct snapraid_handle* handle)
     334             : {
     335             :         int ret;
     336             : 
     337             :         /* do nothing if not opened */
     338      121230 :         if (handle->f == -1)
     339           0 :                 return 0;
     340             : 
     341      121230 :         ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
     342             : 
     343      121230 :         if (ret != 0) {
     344             :                 /* LCOV_EXCL_START */
     345             :                 log_fatal("Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno));
     346             :                 return -1;
     347             :                 /* LCOV_EXCL_STOP */
     348             :         }
     349             : 
     350      121230 :         return 0;
     351             : }
     352             : 
     353         222 : struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
     354             : {
     355             :         tommy_node* i;
     356             :         unsigned j;
     357         222 :         unsigned size = 0;
     358             :         struct snapraid_handle* handle;
     359             : 
     360             :         /* get the size of the mapping */
     361         222 :         size = 0;
     362        1549 :         for (i = state->maplist; i != 0; i = i->next) {
     363        1327 :                 struct snapraid_map* map = i->data;
     364        1327 :                 if (map->position > size)
     365         946 :                         size = map->position;
     366             :         }
     367         222 :         ++size; /* size is one more than the max */
     368             : 
     369         222 :         handle = malloc_nofail(size * sizeof(struct snapraid_handle));
     370             : 
     371        1554 :         for (j = 0; j < size; ++j) {
     372             :                 /* default for empty position */
     373        1332 :                 handle[j].disk = 0;
     374        1332 :                 handle[j].file = 0;
     375        1332 :                 handle[j].f = -1;
     376        1332 :                 handle[j].valid_size = 0;
     377        1332 :                 handle[j].bw = 0;
     378             :         }
     379             : 
     380             :         /* set the vector */
     381        1549 :         for (i = state->disklist; i != 0; i = i->next) {
     382             :                 struct snapraid_map* map;
     383        1327 :                 struct snapraid_disk* disk = i->data;
     384             :                 tommy_node* k;
     385             : 
     386             :                 /* search the mapping for this disk */
     387        1327 :                 map = 0;
     388        4632 :                 for (k = state->maplist; k != 0; k = k->next) {
     389        4632 :                         map = k->data;
     390        4632 :                         if (strcmp(disk->name, map->name) == 0)
     391        1327 :                                 break;
     392             :                 }
     393        1327 :                 if (!map) {
     394             :                         /* LCOV_EXCL_START */
     395             :                         log_fatal("Internal error for inconsistent disk mapping.\n");
     396             :                         os_abort();
     397             :                         /* LCOV_EXCL_STOP */
     398             :                 }
     399             : 
     400        1327 :                 handle[map->position].disk = disk;
     401             :         }
     402             : 
     403         222 :         *handlemax = size;
     404         222 :         return handle;
     405             : }
     406             : 

Generated by: LCOV version 1.0