2026-04-02 16:00:27 +02:00
// SPDX-License-Identifier: GPL-3.0-or-later
2026-04-02 16:25:49 +02:00
// Copyright (C) 2011 Andrea Mazzoleni
2011-03-04 20:59:21 +01:00
2026-06-24 00:15:15 +02:00
# include "os/portable.h"
2011-03-04 20:59:21 +01:00
# include "elem.h"
2015-01-02 18:52:04 +01:00
# include "support.h"
2026-02-02 11:19:51 +01:00
# include "state.h"
2011-03-04 20:59:21 +01:00
# include "util.h"
/****************************************************************************/
/* snapraid */
2026-05-03 22:29:11 +02:00
size_t BLOCK_HASH_SIZE = HASH_MAX ;
2016-04-07 21:39:02 +02:00
2011-09-20 22:35:15 +02:00
struct snapraid_content * content_alloc ( const char * path , uint64_t dev )
2011-04-25 13:43:51 +02:00
{
struct snapraid_content * content ;
content = malloc_nofail ( sizeof ( struct snapraid_content ) ) ;
2011-11-14 19:21:52 +01:00
pathimport ( content - > content , sizeof ( content - > content ) , path ) ;
2011-09-20 22:35:15 +02:00
content - > device = dev ;
2011-04-25 13:43:51 +02:00
return content ;
}
void content_free ( struct snapraid_content * content )
{
free ( content ) ;
}
2026-01-10 16:30:35 +01:00
struct snapraid_filter * filter_alloc_file ( int direction , const char * root , const char * pattern )
2011-03-16 23:52:10 +01:00
{
struct snapraid_filter * filter ;
char * i ;
char * first ;
char * last ;
2015-08-03 19:31:07 +02:00
int token_is_valid ;
int token_is_filled ;
2011-03-16 23:52:10 +01:00
filter = malloc_nofail ( sizeof ( struct snapraid_filter ) ) ;
2011-05-03 23:28:03 +02:00
pathimport ( filter - > pattern , sizeof ( filter - > pattern ) , pattern ) ;
2026-01-10 16:30:35 +01:00
pathimport ( filter - > root , sizeof ( filter - > root ) , root ) ;
2011-04-26 18:51:42 +02:00
filter - > direction = direction ;
2011-03-16 23:52:10 +01:00
/* find first and last slash */
first = 0 ;
last = 0 ;
2026-01-10 16:30:35 +01:00
2015-08-03 19:31:07 +02:00
/* reject invalid tokens, like "<empty>", ".", ".." and more dots */
token_is_valid = 0 ;
token_is_filled = 0 ;
2014-09-27 00:32:39 +02:00
for ( i = filter - > pattern ; * i ; + + i ) {
2011-03-16 23:52:10 +01:00
if ( * i = = ' / ' ) {
2015-08-03 19:31:07 +02:00
/* reject invalid tokens, but accept an empty one as first */
if ( ! token_is_valid & & ( first ! = 0 | | token_is_filled ) ) {
free ( filter ) ;
return 0 ;
}
token_is_valid = 0 ;
token_is_filled = 0 ;
/* update slash position */
2011-03-16 23:52:10 +01:00
if ( ! first )
first = i ;
last = i ;
2015-08-03 19:31:07 +02:00
} else if ( * i ! = ' . ' ) {
token_is_valid = 1 ;
token_is_filled = 1 ;
} else {
token_is_filled = 1 ;
2011-03-16 23:52:10 +01:00
}
}
2015-08-03 19:31:07 +02:00
/* reject invalid tokens, but accept an empty one as last, but not if it's the only one */
if ( ! token_is_valid & & ( first = = 0 | | token_is_filled ) ) {
free ( filter ) ;
return 0 ;
}
2012-10-10 23:32:09 +02:00
/* it's a file filter */
filter - > is_disk = 0 ;
2011-03-16 23:52:10 +01:00
if ( first = = 0 ) {
/* no slash */
2026-01-10 16:57:04 +01:00
filter - > is_abs = 0 ;
2011-03-16 23:52:10 +01:00
filter - > is_dir = 0 ;
} else {
2026-01-10 16:57:04 +01:00
if ( first = = filter - > pattern ) {
/* slash at the start */
filter - > is_abs = 1 ;
} else {
/* no slash at the start */
filter - > is_abs = 0 ;
}
2011-03-16 23:52:10 +01:00
if ( last [ 1 ] = = 0 ) {
2026-01-10 16:57:04 +01:00
/* slash at the end */
2011-03-16 23:52:10 +01:00
filter - > is_dir = 1 ;
2026-01-10 16:57:04 +01:00
last [ 0 ] = 0 ; /* clear the slash */
2011-03-16 23:52:10 +01:00
} else {
2026-01-10 16:57:04 +01:00
/* no slash at the end */
2011-03-16 23:52:10 +01:00
filter - > is_dir = 0 ;
}
}
return filter ;
}
2012-10-10 23:32:09 +02:00
struct snapraid_filter * filter_alloc_disk ( int direction , const char * pattern )
{
struct snapraid_filter * filter ;
filter = malloc_nofail ( sizeof ( struct snapraid_filter ) ) ;
pathimport ( filter - > pattern , sizeof ( filter - > pattern ) , pattern ) ;
filter - > direction = direction ;
/* it's a disk filter */
filter - > is_disk = 1 ;
2026-01-10 16:57:04 +01:00
filter - > is_abs = 0 ;
2012-10-10 23:32:09 +02:00
filter - > is_dir = 0 ;
/* no slash allowed in disk names */
if ( strchr ( filter - > pattern , ' / ' ) ! = 0 ) {
2014-03-20 22:10:19 +01:00
/* LCOV_EXCL_START */
2012-10-10 23:32:09 +02:00
free ( filter ) ;
return 0 ;
2014-03-20 22:10:19 +01:00
/* LCOV_EXCL_STOP */
2012-10-10 23:32:09 +02:00
}
return filter ;
}
2026-01-10 16:30:35 +01:00
void filter_free ( void * filter )
2011-03-16 23:52:10 +01:00
{
free ( filter ) ;
}
2015-01-27 23:32:13 +01:00
const char * filter_type ( struct snapraid_filter * filter , char * out , size_t out_size )
2011-03-16 23:52:10 +01:00
{
2015-01-27 23:32:13 +01:00
const char * direction ;
2016-04-10 22:44:40 +02:00
2015-01-27 23:32:13 +01:00
if ( filter - > direction < 0 )
direction = " exclude " ;
else
direction = " include " ;
if ( filter - > is_disk )
pathprint ( out , out_size , " %s %s: " , direction , filter - > pattern ) ;
else if ( filter - > is_dir )
pathprint ( out , out_size , " %s %s/ " , direction , filter - > pattern ) ;
else
pathprint ( out , out_size , " %s %s " , direction , filter - > pattern ) ;
return out ;
2015-01-27 23:10:38 +01:00
}
2026-02-27 00:53:39 +01:00
/**
* Apply a filter to the specified path
*
* @ param filter The filter to apply .
* @ param reason Assign to it the filter that caused rejection .
* @ param path The path to filter .
* @ param is_dir if the path refers to a directory , otherwise to a file .
* @ return < 0 if the path is excluded , > 0 if the path is included , 0 if the filter doesn ' t apply .
*/
2026-01-10 16:57:04 +01:00
static int filter_apply ( struct snapraid_filter * filter , struct snapraid_filter * * reason , const char * path , int is_dir )
2015-01-27 23:10:38 +01:00
{
2026-01-10 16:57:04 +01:00
if ( filter - > root [ 0 ] ! = 0 ) { /* if it's a local filter */
if ( ! path_is_root_of ( filter - > root , path ) ) /* applies it only if it matches the root */
return 0 ;
/* advance the path to compare to make it local */
path + = strlen ( filter - > root ) ;
}
2015-01-27 23:10:38 +01:00
2026-04-01 00:32:07 +02:00
/* if the filter is for files, it doesn't applies to directories */
2011-03-16 23:52:10 +01:00
if ( ! filter - > is_dir & & is_dir )
2011-04-26 18:51:42 +02:00
return 0 ;
2011-03-16 23:52:10 +01:00
2026-04-01 00:32:07 +02:00
/*
* If the filter is for directories , it should be applied to all files inside
* that directory .
*
* This is done allowing a partial matching as far it ends at a directory separator
*/
int match_sub = 0 ;
if ( filter - > is_dir & & ! is_dir )
match_sub = 1 ;
2026-01-10 16:57:04 +01:00
int ret = 0 ;
if ( filter - > is_abs ) {
/* skip initial slash, as always missing in the path */
2026-04-01 00:32:07 +02:00
if ( wnmatch_sub ( filter - > pattern + 1 , path , match_sub ) = = 0 )
2015-01-27 23:10:38 +01:00
ret = filter - > direction ;
2011-03-16 23:52:10 +01:00
} else {
2026-02-27 00:53:39 +01:00
/* the path is relative, first try to match from the root */
2026-04-01 00:32:07 +02:00
if ( wnmatch_sub ( filter - > pattern , path , match_sub ) = = 0 ) {
2015-01-27 23:10:38 +01:00
ret = filter - > direction ;
2026-01-10 16:57:04 +01:00
} else {
/* then try to match after all the / presents */
2026-04-23 12:31:08 +02:00
const char * slash = strchr ( path , ' / ' ) ;
2026-01-10 16:57:04 +01:00
while ( slash ) {
2026-04-01 00:32:07 +02:00
if ( wnmatch_sub ( filter - > pattern , slash + 1 , match_sub ) = = 0 ) {
2026-01-10 16:57:04 +01:00
ret = filter - > direction ;
break ;
}
slash = strchr ( slash + 1 , ' / ' ) ;
}
}
2011-03-16 23:52:10 +01:00
}
2015-01-27 23:10:38 +01:00
if ( reason ! = 0 & & ret < 0 )
* reason = filter ;
return ret ;
2011-03-16 23:52:10 +01:00
}
2026-02-27 00:53:39 +01:00
/**
* Apply a list of filter to a specific disk / sub path
*
* @ param filterlist The list of filter to apply
* @ param reason Assign to it the filter that caused rejection .
* @ param disk The disk that contain the path
* @ param sub The path in the disk
* @ param is_dir If the path refers to a directory , otherwise it ' s a file
* @ param is_def_include If no filter apply , if by default force the inclusion . Used to include directory by default .
* @ return < 0 if the path is excluded , = = 0 if the path is included .
*/
2016-05-14 21:59:33 +02:00
static int filter_element ( tommy_list * filterlist , struct snapraid_filter * * reason , const char * disk , const char * sub , int is_dir , int is_def_include )
2011-04-26 18:51:42 +02:00
{
tommy_node * i ;
2026-02-27 00:53:39 +01:00
/**
* The rule is that the latest filter defines the behaviour of paths
* that doesn ' t match any filter .
*
* If the latest filter is an ' exclusion ' filter , the default is to include .
* If the latest filter is an ' inclusion ' filter , the default is to exclude .
* If there are no filter , the default is to include .
*/
int default_direction = 1 ;
2011-04-26 18:51:42 +02:00
/* for each filter */
2014-09-27 00:32:39 +02:00
for ( i = tommy_list_head ( filterlist ) ; i ! = 0 ; i = i - > next ) {
2011-04-26 18:51:42 +02:00
int ret ;
struct snapraid_filter * filter = i - > data ;
2012-10-10 23:32:09 +02:00
if ( filter - > is_disk ) {
2026-01-26 22:50:27 +01:00
if ( wnmatch ( filter - > pattern , disk ) = = 0 ) {
2012-10-10 23:32:09 +02:00
ret = filter - > direction ;
2026-01-26 22:50:27 +01:00
} else {
2012-10-10 23:32:09 +02:00
ret = 0 ;
2026-01-26 22:50:27 +01:00
}
2015-01-27 23:10:38 +01:00
if ( reason ! = 0 & & ret < 0 )
* reason = filter ;
2012-10-10 23:32:09 +02:00
} else {
2026-01-10 16:57:04 +01:00
ret = filter_apply ( filter , reason , sub , is_dir ) ;
2012-10-10 23:32:09 +02:00
}
2011-04-26 18:51:42 +02:00
if ( ret > 0 ) {
/* include the file */
2012-07-03 21:51:50 +02:00
return 0 ;
2011-04-26 18:51:42 +02:00
} else if ( ret < 0 ) {
/* exclude the file */
2012-07-03 21:51:50 +02:00
return - 1 ;
2026-01-26 22:50:27 +01:00
} else {
2026-02-27 00:53:39 +01:00
/* default is opposite of the last filter */
2026-01-10 16:30:35 +01:00
default_direction = - filter - > direction ;
if ( reason ! = 0 & & default_direction < 0 )
2015-01-27 23:10:38 +01:00
* reason = filter ;
2011-04-26 18:51:42 +02:00
/* continue with the next one */
}
}
2026-04-27 15:43:06 +02:00
/*
* Directories are always included by default , otherwise we cannot apply rules
* to the contained files
*/
2016-05-14 21:59:33 +02:00
if ( is_def_include )
2012-06-29 20:35:43 +02:00
return 0 ;
2026-01-26 22:50:27 +01:00
/* files are excluded/included depending of the last rule processed */
2026-01-10 16:30:35 +01:00
if ( default_direction < 0 )
2011-04-26 18:51:42 +02:00
return - 1 ;
2011-03-30 17:02:02 +02:00
2011-04-26 18:51:42 +02:00
return 0 ;
2011-03-30 00:14:43 +02:00
}
2015-01-27 23:10:38 +01:00
int filter_path ( tommy_list * filterlist , struct snapraid_filter * * reason , const char * disk , const char * sub )
2011-09-18 22:43:41 +02:00
{
2016-05-14 21:59:33 +02:00
return filter_element ( filterlist , reason , disk , sub , 0 , 0 ) ;
2011-09-18 22:43:41 +02:00
}
2016-05-14 21:59:33 +02:00
int filter_subdir ( tommy_list * filterlist , struct snapraid_filter * * reason , const char * disk , const char * sub )
2011-09-18 22:43:41 +02:00
{
2016-05-14 21:59:33 +02:00
return filter_element ( filterlist , reason , disk , sub , 1 , 1 ) ;
}
int filter_emptydir ( tommy_list * filterlist , struct snapraid_filter * * reason , const char * disk , const char * sub )
{
return filter_element ( filterlist , reason , disk , sub , 1 , 0 ) ;
2011-09-18 22:43:41 +02:00
}
2013-03-01 18:29:04 +01:00
int filter_existence ( int filter_missing , const char * dir , const char * sub )
2013-02-24 11:33:09 +01:00
{
2013-03-01 18:29:04 +01:00
char path [ PATH_MAX ] ;
struct stat st ;
if ( ! filter_missing )
return 0 ;
/* we directly check if in the disk the file is present or not */
pathprint ( path , sizeof ( path ) , " %s%s " , dir , sub ) ;
2014-09-27 00:32:39 +02:00
2013-03-01 18:29:04 +01:00
if ( lstat ( path , & st ) ! = 0 ) {
/* if the file doesn't exist, we don't filter it out */
if ( errno = = ENOENT )
return 0 ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error in stat file '%s'. %s. \n " , path , strerror ( errno ) ) ;
2013-03-01 18:29:04 +01:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-03-01 18:29:04 +01:00
}
2013-08-18 19:24:55 +02:00
/* the file is present, so we filter it out */
return 1 ;
}
2015-10-01 18:19:48 +02:00
int filter_correctness ( int filter_error , tommy_arrayblkof * infoarr , struct snapraid_disk * disk , struct snapraid_file * file )
2013-08-18 19:24:55 +02:00
{
2026-08-14 00:17:58 +02:00
block_off_t i ;
2013-08-18 19:24:55 +02:00
if ( ! filter_error )
return 0 ;
/* check each block of the file */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2015-10-01 18:19:48 +02:00
block_off_t parity_pos = fs_file2par_get ( disk , file , i ) ;
snapraid_info info = info_get ( infoarr , parity_pos ) ;
2013-08-18 19:24:55 +02:00
/* if the file has a bad block, don't exclude it */
if ( info_get_bad ( info ) )
return 0 ;
}
/* the file is correct, so we filter it out */
2013-03-01 18:29:04 +01:00
return 1 ;
2013-02-24 11:33:09 +01:00
}
2026-05-11 23:24:36 +02:00
int filter_content ( tommy_list * contentlist , const char * mount_point , size_t mount_point_len , const char * sub , size_t sub_len , const char * name )
2011-09-18 22:43:41 +02:00
{
tommy_node * i ;
2026-05-11 23:24:36 +02:00
size_t name_len = strlen ( name ) ;
2014-09-27 00:32:39 +02:00
for ( i = tommy_list_head ( contentlist ) ; i ! = 0 ; i = i - > next ) {
2011-09-18 22:43:41 +02:00
struct snapraid_content * content = i - > data ;
2026-05-11 23:24:36 +02:00
/* if the mount point doesn't match, it's a different disk */
if ( pathncmp ( content - > content , mount_point , mount_point_len ) ! = 0 )
continue ;
/* if the sub dir doesn't match, it's a different dir */
if ( pathncmp ( content - > content + mount_point_len , sub , sub_len ) ! = 0 )
continue ;
/* if the name doesn't match, it's a different name */
if ( pathncmp ( content - > content + mount_point_len + sub_len , name , name_len ) ! = 0 )
continue ;
/* remaining part */
const char * postfix = content - > content + mount_point_len + sub_len + name_len ;
/* if it's an exact match */
if ( * postfix = = 0 )
2011-09-18 22:43:41 +02:00
return - 1 ;
2012-04-14 14:03:49 +02:00
/* exclude also the ".tmp" copy used to save it */
2026-05-11 23:24:36 +02:00
if ( pathcmp ( postfix , " .tmp " ) = = 0 )
2012-04-14 14:03:49 +02:00
return - 1 ;
2014-06-13 18:23:24 +02:00
/* exclude also the ".lock" file */
2026-05-11 23:24:36 +02:00
if ( pathcmp ( postfix , " .lock " ) = = 0 )
2014-06-13 18:23:24 +02:00
return - 1 ;
2011-09-18 22:43:41 +02:00
}
return 0 ;
}
2026-08-14 09:47:12 +02:00
int filter_snapshot ( int enable , const char * sub , const char * name )
2026-05-11 23:24:36 +02:00
{
2026-08-14 09:47:12 +02:00
if ( ! enable )
return 0 ;
2026-05-11 23:24:36 +02:00
if ( * sub = = 0 & & pathcmp ( name , SNAPSHOT_CONTAINER ) = = 0 )
return - 1 ;
return 0 ;
}
2014-10-04 10:27:23 +02:00
struct snapraid_file * file_alloc ( unsigned block_size , const char * sub , data_off_t size , uint64_t mtime_sec , int mtime_nsec , uint64_t inode , uint64_t physical )
2011-03-04 20:59:21 +01:00
{
struct snapraid_file * file ;
2011-03-12 10:09:59 +01:00
block_off_t i ;
2011-03-04 20:59:21 +01:00
file = malloc_nofail ( sizeof ( struct snapraid_file ) ) ;
2013-02-26 21:26:45 +01:00
file - > sub = strdup_nofail ( sub ) ;
2011-03-04 20:59:21 +01:00
file - > size = size ;
file - > blockmax = ( size + block_size - 1 ) / block_size ;
2012-07-10 23:24:31 +02:00
file - > mtime_sec = mtime_sec ;
file - > mtime_nsec = mtime_nsec ;
2011-03-20 22:36:49 +01:00
file - > inode = inode ;
2013-06-25 22:41:10 +02:00
file - > physical = physical ;
2011-04-28 23:09:19 +02:00
file - > flag = 0 ;
2026-03-18 11:51:52 +01:00
file - > shared_flag = 0 ;
2026-07-06 18:38:50 +02:00
file - > blockvec = nalloc_nofail ( file - > blockmax , block_sizeof ( ) ) ;
2011-03-04 20:59:21 +01:00
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2016-04-07 21:39:02 +02:00
struct snapraid_block * block = file_block ( file , i ) ;
block_state_set ( block , BLOCK_STATE_CHG ) ;
hash_invalid_set ( block - > hash ) ;
2011-03-04 20:59:21 +01:00
}
return file ;
}
2015-10-01 18:19:48 +02:00
struct snapraid_file * file_dup ( struct snapraid_file * copy )
{
struct snapraid_file * file ;
block_off_t i ;
file = malloc_nofail ( sizeof ( struct snapraid_file ) ) ;
file - > sub = strdup_nofail ( copy - > sub ) ;
file - > size = copy - > size ;
file - > blockmax = copy - > blockmax ;
file - > mtime_sec = copy - > mtime_sec ;
file - > mtime_nsec = copy - > mtime_nsec ;
file - > inode = copy - > inode ;
file - > physical = copy - > physical ;
file - > flag = copy - > flag ;
2026-07-16 23:22:15 +02:00
file - > shared_flag = 0 ;
2026-07-06 18:38:50 +02:00
file - > blockvec = nalloc_nofail ( file - > blockmax , block_sizeof ( ) ) ;
2015-10-01 18:19:48 +02:00
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2016-04-07 21:39:02 +02:00
struct snapraid_block * block = file_block ( file , i ) ;
struct snapraid_block * copy_block = file_block ( copy , i ) ;
block - > state = copy_block - > state ;
memcpy ( block - > hash , copy_block - > hash , BLOCK_HASH_SIZE ) ;
2015-10-01 18:19:48 +02:00
}
return file ;
}
2011-03-04 20:59:21 +01:00
void file_free ( struct snapraid_file * file )
{
2013-02-26 21:26:45 +01:00
free ( file - > sub ) ;
2015-10-01 18:19:48 +02:00
file - > sub = 0 ;
2011-03-04 20:59:21 +01:00
free ( file - > blockvec ) ;
2015-10-01 18:19:48 +02:00
file - > blockvec = 0 ;
2011-03-04 20:59:21 +01:00
free ( file ) ;
}
2011-03-30 00:14:43 +02:00
2013-02-26 21:26:45 +01:00
void file_rename ( struct snapraid_file * file , const char * sub )
{
free ( file - > sub ) ;
file - > sub = strdup_nofail ( sub ) ;
}
2014-02-12 20:02:35 +01:00
void file_copy ( struct snapraid_file * src_file , struct snapraid_file * dst_file )
{
block_off_t i ;
if ( src_file - > size ! = dst_file - > size ) {
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency: Copy file with different size \n " ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-02-12 20:02:35 +01:00
/* LCOV_EXCL_STOP */
}
2014-05-15 23:45:51 +02:00
if ( src_file - > mtime_sec ! = dst_file - > mtime_sec ) {
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency: Copy file with different mtime_sec \n " ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-05-15 23:45:51 +02:00
/* LCOV_EXCL_STOP */
}
if ( src_file - > mtime_nsec ! = dst_file - > mtime_nsec ) {
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency: Copy file with different mtime_nsec \n " ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-05-15 23:45:51 +02:00
/* LCOV_EXCL_STOP */
}
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < dst_file - > blockmax ; + + i ) {
2014-02-12 20:02:35 +01:00
/* set a block with hash computed but without parity */
2016-04-07 21:39:02 +02:00
block_state_set ( file_block ( dst_file , i ) , BLOCK_STATE_REP ) ;
2014-02-12 20:02:35 +01:00
/* copy the hash */
2016-04-07 21:39:02 +02:00
memcpy ( file_block ( dst_file , i ) - > hash , file_block ( src_file , i ) - > hash , BLOCK_HASH_SIZE ) ;
2014-02-12 20:02:35 +01:00
}
2014-05-15 23:45:51 +02:00
file_flag_set ( dst_file , FILE_IS_COPY ) ;
2014-02-12 20:02:35 +01:00
}
const char * file_name ( const struct snapraid_file * file )
2011-03-30 00:14:43 +02:00
{
const char * r = strrchr ( file - > sub , ' / ' ) ;
2014-09-27 00:32:39 +02:00
2011-03-30 00:14:43 +02:00
if ( ! r )
r = file - > sub ;
else
+ + r ;
return r ;
}
2011-03-04 20:59:21 +01:00
2026-06-04 00:34:51 +02:00
size_t file_block_size ( struct snapraid_file * file , block_off_t file_pos , size_t block_size )
2015-10-03 10:43:02 +02:00
{
/* if it's the last block */
if ( file_pos + 1 = = file - > blockmax ) {
2026-06-04 00:34:51 +02:00
size_t block_remainder ;
2015-10-03 10:43:02 +02:00
if ( file - > size = = 0 )
return 0 ;
2015-10-08 22:19:52 +02:00
block_remainder = file - > size % block_size ;
if ( block_remainder = = 0 )
block_remainder = block_size ;
return block_remainder ;
2015-10-03 10:43:02 +02:00
}
return block_size ;
}
int file_block_is_last ( struct snapraid_file * file , block_off_t file_pos )
{
if ( file_pos = = 0 & & file - > blockmax = = 0 )
return 1 ;
if ( file_pos > = file - > blockmax ) {
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency: File block position over the max \n " ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-10-03 10:43:02 +02:00
/* LCOV_EXCL_STOP */
}
return file_pos = = file - > blockmax - 1 ;
}
2013-06-25 22:41:10 +02:00
int file_inode_compare_to_arg ( const void * void_arg , const void * void_data )
2011-03-04 20:59:21 +01:00
{
2011-03-20 22:36:49 +01:00
const uint64_t * arg = void_arg ;
2011-03-04 20:59:21 +01:00
const struct snapraid_file * file = void_data ;
2014-09-27 00:32:39 +02:00
2011-03-20 22:36:49 +01:00
if ( * arg < file - > inode )
return - 1 ;
if ( * arg > file - > inode )
return 1 ;
return 0 ;
2011-03-04 20:59:21 +01:00
}
2013-06-25 22:41:10 +02:00
int file_inode_compare ( const void * void_a , const void * void_b )
{
const struct snapraid_file * file_a = void_a ;
const struct snapraid_file * file_b = void_b ;
2014-09-27 00:32:39 +02:00
2013-06-25 22:41:10 +02:00
if ( file_a - > inode < file_b - > inode )
return - 1 ;
if ( file_a - > inode > file_b - > inode )
return 1 ;
return 0 ;
}
2014-02-12 20:02:35 +01:00
int file_path_compare ( const void * void_a , const void * void_b )
2013-06-25 22:41:10 +02:00
{
const struct snapraid_file * file_a = void_a ;
const struct snapraid_file * file_b = void_b ;
2014-09-27 00:32:39 +02:00
2013-06-25 22:41:10 +02:00
return strcmp ( file_a - > sub , file_b - > sub ) ;
}
int file_physical_compare ( const void * void_a , const void * void_b )
{
const struct snapraid_file * file_a = void_a ;
const struct snapraid_file * file_b = void_b ;
2014-09-27 00:32:39 +02:00
2013-06-25 22:41:10 +02:00
if ( file_a - > physical < file_b - > physical )
return - 1 ;
if ( file_a - > physical > file_b - > physical )
return 1 ;
return 0 ;
}
2014-02-12 20:02:35 +01:00
int file_path_compare_to_arg ( const void * void_arg , const void * void_data )
2011-10-09 23:41:08 +02:00
{
const char * arg = void_arg ;
const struct snapraid_file * file = void_data ;
2014-09-27 00:32:39 +02:00
2011-10-09 23:41:08 +02:00
return strcmp ( arg , file - > sub ) ;
}
2014-02-12 20:02:35 +01:00
int file_name_compare ( const void * void_a , const void * void_b )
{
const struct snapraid_file * file_a = void_a ;
const struct snapraid_file * file_b = void_b ;
const char * name_a = file_name ( file_a ) ;
const char * name_b = file_name ( file_b ) ;
return strcmp ( name_a , name_b ) ;
}
int file_stamp_compare ( const void * void_a , const void * void_b )
{
const struct snapraid_file * file_a = void_a ;
const struct snapraid_file * file_b = void_b ;
if ( file_a - > size < file_b - > size )
return - 1 ;
if ( file_a - > size > file_b - > size )
return 1 ;
if ( file_a - > mtime_sec < file_b - > mtime_sec )
return - 1 ;
if ( file_a - > mtime_sec > file_b - > mtime_sec )
return 1 ;
if ( file_a - > mtime_nsec < file_b - > mtime_nsec )
return - 1 ;
if ( file_a - > mtime_nsec > file_b - > mtime_nsec )
return 1 ;
return 0 ;
}
int file_namestamp_compare ( const void * void_a , const void * void_b )
{
int ret ;
ret = file_name_compare ( void_a , void_b ) ;
if ( ret ! = 0 )
return ret ;
return file_stamp_compare ( void_a , void_b ) ;
}
2014-05-15 23:45:51 +02:00
int file_pathstamp_compare ( const void * void_a , const void * void_b )
{
int ret ;
ret = file_path_compare ( void_a , void_b ) ;
if ( ret ! = 0 )
return ret ;
return file_stamp_compare ( void_a , void_b ) ;
}
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent_alloc ( block_off_t parity_pos , struct snapraid_file * file , block_off_t file_pos , block_off_t count )
2015-10-04 22:02:05 +02:00
{
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent ;
2015-10-04 22:02:05 +02:00
2015-11-18 22:10:50 +01:00
if ( count = = 0 ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Allocating empty extent for file '%s' at position '% " PRIu64 " /% " PRIu64 " ' \n " , file - > sub , file_pos , file - > blockmax ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-11-18 22:10:50 +01:00
/* LCOV_EXCL_STOP */
}
2026-06-03 19:00:24 +02:00
if ( count > file - > blockmax | | file_pos > file - > blockmax - count ) {
2015-11-18 22:10:50 +01:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Allocating overflowing extent for file '%s' at position '% " PRIu64 " :% " PRIu64 " /% " PRIu64 " ' \n " , file - > sub , file_pos , count , file - > blockmax ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-11-18 22:10:50 +01:00
/* LCOV_EXCL_STOP */
}
2015-11-16 22:54:00 +01:00
2016-05-23 22:10:05 +02:00
extent = malloc_nofail ( sizeof ( struct snapraid_extent ) ) ;
extent - > parity_pos = parity_pos ;
extent - > file = file ;
extent - > file_pos = file_pos ;
extent - > count = count ;
2015-10-04 22:02:05 +02:00
2016-05-23 22:10:05 +02:00
return extent ;
2015-10-04 22:02:05 +02:00
}
2016-05-23 22:10:05 +02:00
void extent_free ( struct snapraid_extent * extent )
2015-10-04 22:02:05 +02:00
{
2016-05-23 22:10:05 +02:00
free ( extent ) ;
2015-10-04 22:02:05 +02:00
}
2016-05-23 22:10:05 +02:00
int extent_parity_compare ( const void * void_a , const void * void_b )
2015-10-04 22:02:05 +02:00
{
2016-05-23 22:10:05 +02:00
const struct snapraid_extent * arg_a = void_a ;
const struct snapraid_extent * arg_b = void_b ;
2015-10-04 22:02:05 +02:00
if ( arg_a - > parity_pos < arg_b - > parity_pos )
return - 1 ;
if ( arg_a - > parity_pos > arg_b - > parity_pos )
return 1 ;
return 0 ;
}
2016-05-23 22:10:05 +02:00
int extent_file_compare ( const void * void_a , const void * void_b )
2015-10-04 22:02:05 +02:00
{
2016-05-23 22:10:05 +02:00
const struct snapraid_extent * arg_a = void_a ;
const struct snapraid_extent * arg_b = void_b ;
2026-08-17 10:07:41 +02:00
uintptr_t file_a = ( uintptr_t ) arg_a - > file ;
uintptr_t file_b = ( uintptr_t ) arg_b - > file ;
2015-10-04 22:02:05 +02:00
2026-08-17 10:07:41 +02:00
/* cast to uintptr_t to avoid undefined behavior when comparing pointers to distinct heap allocations */
if ( file_a < file_b )
2015-10-04 22:02:05 +02:00
return - 1 ;
2026-08-17 10:07:41 +02:00
if ( file_a > file_b )
2015-10-04 22:02:05 +02:00
return 1 ;
if ( arg_a - > file_pos < arg_b - > file_pos )
return - 1 ;
if ( arg_a - > file_pos > arg_b - > file_pos )
return 1 ;
return 0 ;
}
2013-02-28 21:56:02 +01:00
struct snapraid_link * link_alloc ( const char * sub , const char * linkto , unsigned link_flag )
2011-08-26 14:18:38 +02:00
{
2015-10-08 22:19:52 +02:00
struct snapraid_link * slink ;
2011-08-26 14:18:38 +02:00
2015-10-08 22:19:52 +02:00
slink = malloc_nofail ( sizeof ( struct snapraid_link ) ) ;
slink - > sub = strdup_nofail ( sub ) ;
slink - > linkto = strdup_nofail ( linkto ) ;
slink - > flag = link_flag ;
2011-08-26 14:18:38 +02:00
2015-10-08 22:19:52 +02:00
return slink ;
2011-08-26 14:18:38 +02:00
}
2015-10-08 22:19:52 +02:00
void link_free ( struct snapraid_link * slink )
2011-08-26 14:18:38 +02:00
{
2015-10-08 22:19:52 +02:00
free ( slink - > sub ) ;
free ( slink - > linkto ) ;
free ( slink ) ;
2011-08-26 14:18:38 +02:00
}
2013-10-19 13:24:35 +02:00
int link_name_compare_to_arg ( const void * void_arg , const void * void_data )
2011-08-26 14:18:38 +02:00
{
const char * arg = void_arg ;
2015-10-08 22:19:52 +02:00
const struct snapraid_link * slink = void_data ;
2014-09-27 00:32:39 +02:00
2015-10-08 22:19:52 +02:00
return strcmp ( arg , slink - > sub ) ;
2011-08-26 14:18:38 +02:00
}
2013-10-19 13:24:35 +02:00
int link_alpha_compare ( const void * void_a , const void * void_b )
{
2015-10-08 22:19:52 +02:00
const struct snapraid_link * slink_a = void_a ;
const struct snapraid_link * slink_b = void_b ;
2014-09-27 00:32:39 +02:00
2015-10-08 22:19:52 +02:00
return strcmp ( slink_a - > sub , slink_b - > sub ) ;
2013-10-19 13:24:35 +02:00
}
2013-01-27 16:44:04 +01:00
struct snapraid_dir * dir_alloc ( const char * sub )
{
struct snapraid_dir * dir ;
dir = malloc_nofail ( sizeof ( struct snapraid_dir ) ) ;
2013-05-30 23:50:26 +02:00
dir - > sub = strdup_nofail ( sub ) ;
2013-01-27 16:44:04 +01:00
dir - > flag = 0 ;
return dir ;
}
void dir_free ( struct snapraid_dir * dir )
{
2013-05-30 23:50:26 +02:00
free ( dir - > sub ) ;
2013-01-27 16:44:04 +01:00
free ( dir ) ;
}
int dir_name_compare ( const void * void_arg , const void * void_data )
{
const char * arg = void_arg ;
const struct snapraid_dir * dir = void_data ;
2014-09-27 00:32:39 +02:00
2013-01-27 16:44:04 +01:00
return strcmp ( arg , dir - > sub ) ;
}
2026-04-27 14:39:38 +02:00
struct snapraid_dealloc * dealloc_alloc ( unsigned block_size , const char * sub , data_off_t size , int64_t mtime_sec , int32_t mtime_nsec )
{
struct snapraid_dealloc * dealloc ;
dealloc = malloc_nofail ( sizeof ( struct snapraid_dealloc ) ) ;
dealloc - > sub = strdup_nofail ( sub ) ;
dealloc - > size = size ;
dealloc - > mtime_sec = mtime_sec ;
dealloc - > mtime_nsec = mtime_nsec ;
dealloc - > blockmax = ( size + block_size - 1 ) / block_size ;
2026-07-06 18:38:50 +02:00
dealloc - > blockhash = nalloc_nofail ( dealloc - > blockmax , BLOCK_HASH_SIZE ) ;
2026-04-27 14:39:38 +02:00
return dealloc ;
}
void dealloc_import ( struct snapraid_dealloc * dealloc , struct snapraid_file * file )
{
assert ( dealloc - > blockmax = = file - > blockmax ) ;
/* for all the blocks of the file */
for ( block_off_t i = 0 ; i < dealloc - > blockmax ; + + i ) {
struct snapraid_block * block = fs_file2block_get ( file , i ) ;
unsigned char * hash = dealloc - > blockhash + i * BLOCK_HASH_SIZE ;
unsigned state = block_state_get ( block ) ;
switch ( state ) {
case BLOCK_STATE_BLK :
case BLOCK_STATE_REP :
memcpy ( hash , block - > hash , BLOCK_HASH_SIZE ) ;
break ;
case BLOCK_STATE_CHG :
hash_invalid_set ( hash ) ;
break ;
default :
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: State for dealloc block % " PRIu64 " state %u \n " , i , state ) ;
2026-04-27 14:39:38 +02:00
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
}
}
void dealloc_free ( struct snapraid_dealloc * dealloc )
{
free ( dealloc - > sub ) ;
free ( dealloc - > blockhash ) ;
free ( dealloc ) ;
}
2021-05-22 10:09:43 +02:00
struct snapraid_disk * disk_alloc ( const char * name , const char * dir , uint64_t dev , const char * uuid , int skip_access )
2011-03-04 20:59:21 +01:00
{
struct snapraid_disk * disk ;
2025-09-15 16:56:09 +02:00
unsigned i ;
2011-03-04 20:59:21 +01:00
disk = malloc_nofail ( sizeof ( struct snapraid_disk ) ) ;
2011-03-16 23:52:10 +01:00
pathcpy ( disk - > name , sizeof ( disk - > name ) , name ) ;
2026-04-21 15:19:25 +02:00
pathimport ( disk - > mount_point , sizeof ( disk - > mount_point ) , dir ) ;
2015-12-28 12:09:13 +01:00
pathcpy ( disk - > uuid , sizeof ( disk - > uuid ) , uuid ) ;
2011-03-12 10:23:42 +01:00
/* ensure that the dir terminate with "/" if it isn't empty */
2026-04-21 15:19:25 +02:00
pathslash ( disk - > mount_point , sizeof ( disk - > mount_point ) ) ;
/* no snapshot by default */
2026-05-04 10:59:12 +02:00
memset ( & disk - > fss , 0 , sizeof ( disk - > fss ) ) ;
2026-04-21 15:19:25 +02:00
pathcpy ( disk - > dir , sizeof ( disk - > dir ) , disk - > mount_point ) ;
2011-03-12 10:23:42 +01:00
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2026-03-11 15:50:49 +01:00
disk - > single_thread = 0 ;
2021-05-25 22:08:12 +02:00
thread_mutex_init ( & disk - > fs_mutex ) ;
2016-03-18 18:45:35 +01:00
# endif
2016-01-01 10:00:18 +01:00
2015-04-08 22:56:44 +02:00
disk - > smartctl [ 0 ] = 0 ;
2026-06-15 14:41:03 +02:00
disk - > smartctl_info [ 0 ] = 0 ;
2026-06-25 22:52:53 +02:00
for ( i = 0 ; i < SMART_IGNORE_MAX ; + + i ) {
disk - > smartignore [ i ] . attr_index = 0 ;
disk - > smartignore [ i ] . attr_name [ 0 ] = 0 ;
}
2026-01-29 18:43:00 +01:00
disk - > fstype [ 0 ] = 0 ;
disk - > fslabel [ 0 ] = 0 ;
2026-04-28 00:35:15 +02:00
disk - > mount_device = dev ;
disk - > dir_device = dev ;
2014-06-25 22:32:36 +02:00
disk - > tick = 0 ;
2017-11-19 19:33:47 +01:00
disk - > cached_blocks = 0 ;
disk - > progress_file = 0 ;
2014-11-09 19:14:29 +01:00
disk - > total_blocks = 0 ;
disk - > free_blocks = 0 ;
2011-03-04 20:59:21 +01:00
disk - > first_free_block = 0 ;
2013-12-14 17:54:04 +01:00
disk - > has_volatile_inodes = 0 ;
2017-10-29 18:07:01 +01:00
disk - > has_volatile_hardlinks = 0 ;
2013-12-14 17:54:04 +01:00
disk - > has_unreliable_physical = 0 ;
disk - > has_different_uuid = 0 ;
2015-12-28 12:09:13 +01:00
disk - > has_unsupported_uuid = * uuid = = 0 ; /* empty UUID means unsupported */
2014-01-04 17:42:45 +01:00
disk - > had_empty_uuid = 0 ;
2014-11-09 10:51:48 +01:00
disk - > mapping_idx = - 1 ;
2021-05-22 10:09:43 +02:00
disk - > skip_access = skip_access ;
2011-03-04 20:59:21 +01:00
tommy_list_init ( & disk - > filelist ) ;
2013-02-24 19:02:53 +01:00
tommy_list_init ( & disk - > deletedlist ) ;
2011-03-20 22:36:49 +01:00
tommy_hashdyn_init ( & disk - > inodeset ) ;
2011-10-09 23:41:08 +02:00
tommy_hashdyn_init ( & disk - > pathset ) ;
2014-02-12 20:02:35 +01:00
tommy_hashdyn_init ( & disk - > stampset ) ;
2011-08-26 14:18:38 +02:00
tommy_list_init ( & disk - > linklist ) ;
tommy_hashdyn_init ( & disk - > linkset ) ;
2013-01-27 16:44:04 +01:00
tommy_list_init ( & disk - > dirlist ) ;
tommy_hashdyn_init ( & disk - > dirset ) ;
2026-04-27 14:39:38 +02:00
tommy_list_init ( & disk - > dealloclist ) ;
2016-05-23 22:10:05 +02:00
tommy_tree_init ( & disk - > fs_parity , extent_parity_compare ) ;
tommy_tree_init ( & disk - > fs_file , extent_file_compare ) ;
2015-10-04 23:47:06 +02:00
disk - > fs_last = 0 ;
2011-03-04 20:59:21 +01:00
return disk ;
}
void disk_free ( struct snapraid_disk * disk )
{
2011-12-08 20:37:27 +01:00
tommy_list_foreach ( & disk - > filelist , ( tommy_foreach_func * ) file_free ) ;
2015-10-01 23:22:21 +02:00
tommy_list_foreach ( & disk - > deletedlist , ( tommy_foreach_func * ) file_free ) ;
2016-05-23 22:10:05 +02:00
tommy_tree_foreach ( & disk - > fs_file , ( tommy_foreach_func * ) extent_free ) ;
2011-03-20 22:36:49 +01:00
tommy_hashdyn_done ( & disk - > inodeset ) ;
2011-10-09 23:41:08 +02:00
tommy_hashdyn_done ( & disk - > pathset ) ;
2014-02-12 20:02:35 +01:00
tommy_hashdyn_done ( & disk - > stampset ) ;
2011-12-08 20:37:27 +01:00
tommy_list_foreach ( & disk - > linklist , ( tommy_foreach_func * ) link_free ) ;
2011-08-26 14:18:38 +02:00
tommy_hashdyn_done ( & disk - > linkset ) ;
2013-01-27 16:44:04 +01:00
tommy_list_foreach ( & disk - > dirlist , ( tommy_foreach_func * ) dir_free ) ;
tommy_hashdyn_done ( & disk - > dirset ) ;
2026-04-27 14:39:38 +02:00
tommy_list_foreach ( & disk - > dealloclist , ( tommy_foreach_func * ) dealloc_free ) ;
2016-01-01 10:00:18 +01:00
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2016-02-13 15:35:23 +01:00
thread_mutex_destroy ( & disk - > fs_mutex ) ;
2016-03-18 18:45:35 +01:00
# endif
2016-01-01 10:00:18 +01:00
2011-03-04 20:59:21 +01:00
free ( disk ) ;
}
2011-03-06 22:52:50 +01:00
2026-03-11 13:53:42 +01:00
struct snapraid_extra * extra_alloc ( const char * name , const char * dir , uint64_t dev , const char * uuid )
{
struct snapraid_extra * extra ;
unsigned i ;
extra = malloc_nofail ( sizeof ( struct snapraid_extra ) ) ;
pathcpy ( extra - > name , sizeof ( extra - > name ) , name ) ;
pathimport ( extra - > dir , sizeof ( extra - > dir ) , dir ) ;
pathcpy ( extra - > uuid , sizeof ( extra - > uuid ) , uuid ) ;
/* ensure that the dir terminate with "/" if it isn't empty */
pathslash ( extra - > dir , sizeof ( extra - > dir ) ) ;
extra - > smartctl [ 0 ] = 0 ;
2026-06-15 14:41:03 +02:00
extra - > smartctl_info [ 0 ] = 0 ;
2026-06-25 22:52:53 +02:00
for ( i = 0 ; i < SMART_IGNORE_MAX ; + + i ) {
extra - > smartignore [ i ] . attr_index = 0 ;
extra - > smartignore [ i ] . attr_name [ 0 ] = 0 ;
}
2026-03-11 13:53:42 +01:00
extra - > fstype [ 0 ] = 0 ;
extra - > fslabel [ 0 ] = 0 ;
extra - > device = dev ;
return extra ;
}
void extra_free ( struct snapraid_extra * extra )
{
free ( extra ) ;
}
2016-01-01 10:00:18 +01:00
static inline void fs_lock ( struct snapraid_disk * disk )
{
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2026-03-11 15:50:49 +01:00
if ( ! disk - > single_thread )
thread_mutex_lock ( & disk - > fs_mutex ) ;
2016-03-18 18:45:35 +01:00
# else
( void ) disk ;
# endif
2016-01-01 10:00:18 +01:00
}
static inline void fs_unlock ( struct snapraid_disk * disk )
{
2021-05-25 22:08:12 +02:00
# if HAVE_THREAD
2026-03-11 15:50:49 +01:00
if ( ! disk - > single_thread )
thread_mutex_unlock ( & disk - > fs_mutex ) ;
2016-03-18 18:45:35 +01:00
# else
( void ) disk ;
# endif
2016-01-01 10:00:18 +01:00
}
2016-05-23 22:10:05 +02:00
struct extent_disk_empty {
2015-10-04 22:02:05 +02:00
block_off_t blockmax ;
} ;
2015-10-01 18:19:48 +02:00
2015-10-04 22:02:05 +02:00
/**
2016-05-23 22:10:05 +02:00
* Compare the extent if inside the specified blockmax .
2015-10-04 22:02:05 +02:00
*/
2016-05-23 22:10:05 +02:00
static int extent_disk_empty_compare_unlock ( const void * void_a , const void * void_b )
2015-10-01 18:19:48 +02:00
{
2016-05-23 22:10:05 +02:00
const struct extent_disk_empty * arg_a = void_a ;
const struct snapraid_extent * arg_b = void_b ;
2015-10-01 18:19:48 +02:00
2015-10-04 22:02:05 +02:00
/* if the block is inside the specified blockmax, it's found */
if ( arg_a - > blockmax > arg_b - > parity_pos )
return 0 ;
2015-10-01 18:19:48 +02:00
2015-10-04 22:02:05 +02:00
/* otherwise search for a smaller one */
return - 1 ;
2015-10-01 18:19:48 +02:00
}
2016-01-01 10:00:18 +01:00
int fs_is_empty ( struct snapraid_disk * disk , block_off_t blockmax )
2015-10-01 18:19:48 +02:00
{
2016-05-23 22:10:05 +02:00
struct extent_disk_empty arg = { blockmax } ;
2015-10-01 18:19:48 +02:00
2026-04-27 15:43:06 +02:00
/*
* If there is an element , it ' s not empty
* even if links and dirs have no block allocation
*/
2015-10-04 22:02:05 +02:00
if ( ! tommy_list_empty ( & disk - > filelist ) )
return 0 ;
if ( ! tommy_list_empty ( & disk - > linklist ) )
return 0 ;
if ( ! tommy_list_empty ( & disk - > dirlist ) )
2015-10-01 18:19:48 +02:00
return 0 ;
2016-01-01 10:00:18 +01:00
fs_lock ( disk ) ;
2016-05-23 22:10:05 +02:00
/* search for any extent inside blockmax */
if ( tommy_tree_search_compare ( & disk - > fs_parity , extent_disk_empty_compare_unlock , & arg ) ! = 0 ) {
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
return 0 ;
2016-01-01 10:00:18 +01:00
}
2015-10-03 10:43:02 +02:00
2015-10-04 22:02:05 +02:00
/* finally, it's empty */
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
return 1 ;
2015-10-01 18:19:48 +02:00
}
2016-05-23 22:10:05 +02:00
struct extent_disk_size {
2015-10-04 22:02:05 +02:00
block_off_t size ;
} ;
/**
2016-05-23 22:10:05 +02:00
* Compare the extent by highest parity position .
2015-10-04 22:02:05 +02:00
*
* The maximum parity position is stored as size .
*/
2016-05-23 22:10:05 +02:00
static int extent_disk_size_compare_unlock ( const void * void_a , const void * void_b )
2015-10-01 18:19:48 +02:00
{
2016-05-23 22:10:05 +02:00
struct extent_disk_size * arg_a = ( void * ) void_a ;
const struct snapraid_extent * arg_b = void_b ;
2015-10-01 18:19:48 +02:00
2015-10-04 22:02:05 +02:00
/* get the maximum size */
if ( arg_a - > size < arg_b - > parity_pos + arg_b - > count )
arg_a - > size = arg_b - > parity_pos + arg_b - > count ;
2015-10-01 18:19:48 +02:00
2015-10-04 22:02:05 +02:00
/* search always for a bigger one */
return 1 ;
2015-10-01 18:19:48 +02:00
}
2016-01-01 10:00:18 +01:00
block_off_t fs_size ( struct snapraid_disk * disk )
2015-10-01 18:19:48 +02:00
{
2016-05-23 22:10:05 +02:00
struct extent_disk_size arg = { 0 } ;
2016-01-01 10:00:18 +01:00
fs_lock ( disk ) ;
2016-05-23 22:10:05 +02:00
tommy_tree_search_compare ( & disk - > fs_parity , extent_disk_size_compare_unlock , & arg ) ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
return arg . size ;
}
2015-10-01 18:19:48 +02:00
2016-05-23 22:10:05 +02:00
struct extent_check {
const struct snapraid_extent * prev ;
2015-10-05 21:40:44 +02:00
int result ;
} ;
2016-05-23 22:10:05 +02:00
static void extent_parity_check_foreach_unlock ( void * void_arg , void * void_obj )
2015-10-05 21:40:44 +02:00
{
2016-05-23 22:10:05 +02:00
struct extent_check * arg = void_arg ;
const struct snapraid_extent * obj = void_obj ;
const struct snapraid_extent * prev = arg - > prev ;
2015-10-05 21:40:44 +02:00
/* set the next previous block */
arg - > prev = obj ;
/* stop reporting if too many errors */
if ( arg - > result > 100 ) {
/* LCOV_EXCL_START */
return ;
/* LCOV_EXCL_STOP */
}
2015-11-16 22:54:00 +01:00
if ( obj - > count = = 0 ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Parity count zero for file '%s' at '% " PRIu64 " ' \n " ,
2015-11-16 22:54:00 +01:00
obj - > file - > sub , obj - > parity_pos ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
2015-10-05 21:40:44 +02:00
/* check only if there is a previous block */
if ( ! prev )
return ;
/* check the order */
if ( prev - > parity_pos > = obj - > parity_pos ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Parity order for files '%s' at '% " PRIu64 " :% " PRIu64 " ' and '%s' at '% " PRIu64 " :% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
prev - > file - > sub , prev - > parity_pos , prev - > count , obj - > file - > sub , obj - > parity_pos , obj - > count ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
2016-05-23 22:10:05 +02:00
/* check that the extents don't overlap */
2026-06-03 19:00:24 +02:00
if ( obj - > parity_pos - prev - > parity_pos < prev - > count ) {
2015-10-05 21:40:44 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Parity overlap for files '%s' at '% " PRIu64 " :% " PRIu64 " ' and '%s' at '% " PRIu64 " :% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
prev - > file - > sub , prev - > parity_pos , prev - > count , obj - > file - > sub , obj - > parity_pos , obj - > count ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
}
2016-05-23 22:10:05 +02:00
static void extent_file_check_foreach_unlock ( void * void_arg , void * void_obj )
2015-10-05 21:40:44 +02:00
{
2016-05-23 22:10:05 +02:00
struct extent_check * arg = void_arg ;
const struct snapraid_extent * obj = void_obj ;
const struct snapraid_extent * prev = arg - > prev ;
2015-10-05 21:40:44 +02:00
/* set the next previous block */
arg - > prev = obj ;
/* stop reporting if too many errors */
if ( arg - > result > 100 ) {
/* LCOV_EXCL_START */
return ;
/* LCOV_EXCL_STOP */
}
2015-11-16 22:54:00 +01:00
if ( obj - > count = = 0 ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: File count zero for file '%s' at '% " PRIu64 " ' \n " ,
2015-11-16 22:54:00 +01:00
obj - > file - > sub , obj - > file_pos ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
2016-05-23 22:10:05 +02:00
/* note that for deleted files, some extents may be missing */
2015-10-05 21:40:44 +02:00
/* if the files are different */
if ( ! prev | | prev - > file ! = obj - > file ) {
if ( prev ! = 0 ) {
if ( file_flag_has ( prev - > file , FILE_IS_DELETED ) ) {
2016-05-23 22:10:05 +02:00
/* check that the extent doesn't overflow the file */
2026-06-03 19:00:24 +02:00
if ( prev - > count > prev - > file - > blockmax | | prev - > file_pos > prev - > file - > blockmax - prev - > count ) {
2015-10-05 21:40:44 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Delete end for file '%s' at '% " PRIu64 " :% " PRIu64 " ' overflowing size '% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
prev - > file - > sub , prev - > file_pos , prev - > count , prev - > file - > blockmax ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
} else {
2016-05-23 22:10:05 +02:00
/* check that the extent ends the file */
2015-10-05 21:40:44 +02:00
if ( prev - > file_pos + prev - > count ! = prev - > file - > blockmax ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: File end for file '%s' at '% " PRIu64 " :% " PRIu64 " ' instead of size '% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
prev - > file - > sub , prev - > file_pos , prev - > count , prev - > file - > blockmax ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
}
}
if ( file_flag_has ( obj - > file , FILE_IS_DELETED ) ) {
2016-05-23 22:10:05 +02:00
/* check that the extent doesn't overflow the file */
2026-06-03 19:00:24 +02:00
if ( obj - > count > obj - > file - > blockmax | | obj - > file_pos > obj - > file - > blockmax - obj - > count ) {
2015-10-05 21:40:44 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Delete start for file '%s' at '% " PRIu64 " :% " PRIu64 " ' overflowing size '% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
obj - > file - > sub , obj - > file_pos , obj - > count , obj - > file - > blockmax ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
} else {
2016-05-23 22:10:05 +02:00
/* check that the extent starts the file */
2015-10-05 21:40:44 +02:00
if ( obj - > file_pos ! = 0 ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: File start for file '%s' at '% " PRIu64 " :% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
obj - > file - > sub , obj - > file_pos , obj - > count ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
}
} else {
/* check the order */
if ( prev - > file_pos > = obj - > file_pos ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: File order for file '%s' at '% " PRIu64 " :% " PRIu64 " ' and at '% " PRIu64 " :% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
prev - > file - > sub , prev - > file_pos , prev - > count , obj - > file_pos , obj - > count ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
if ( file_flag_has ( obj - > file , FILE_IS_DELETED ) ) {
2016-05-23 22:10:05 +02:00
/* check that the extents don't overlap */
2026-06-03 19:00:24 +02:00
if ( obj - > file_pos - prev - > file_pos < prev - > count ) {
2015-10-05 21:40:44 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Delete sequence for file '%s' at '% " PRIu64 " :% " PRIu64 " ' and at '% " PRIu64 " :% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
prev - > file - > sub , prev - > file_pos , prev - > count , obj - > file_pos , obj - > count ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
} else {
2016-05-23 22:10:05 +02:00
/* check that the extents are sequential */
2015-10-05 21:40:44 +02:00
if ( prev - > file_pos + prev - > count ! = obj - > file_pos ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: File sequence for file '%s' at '% " PRIu64 " :% " PRIu64 " ' and at '% " PRIu64 " :% " PRIu64 " ' \n " ,
2015-10-05 21:40:44 +02:00
prev - > file - > sub , prev - > file_pos , prev - > count , obj - > file_pos , obj - > count ) ;
+ + arg - > result ;
return ;
/* LCOV_EXCL_STOP */
}
}
}
}
int fs_check ( struct snapraid_disk * disk )
{
2016-05-23 22:10:05 +02:00
struct extent_check arg ;
2015-10-05 21:40:44 +02:00
/* error count starts from 0 */
arg . result = 0 ;
2016-01-01 10:00:18 +01:00
fs_lock ( disk ) ;
2015-10-05 21:40:44 +02:00
/* check parity sequence */
arg . prev = 0 ;
2016-05-23 22:10:05 +02:00
tommy_tree_foreach_arg ( & disk - > fs_parity , extent_parity_check_foreach_unlock , & arg ) ;
2015-10-05 21:40:44 +02:00
/* check file sequence */
arg . prev = 0 ;
2016-05-23 22:10:05 +02:00
tommy_tree_foreach_arg ( & disk - > fs_file , extent_file_check_foreach_unlock , & arg ) ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-05 21:40:44 +02:00
if ( arg . result ! = 0 )
return - 1 ;
return 0 ;
}
2016-05-23 22:10:05 +02:00
struct extent_parity_inside {
2015-10-04 22:02:05 +02:00
block_off_t parity_pos ;
} ;
2015-10-01 18:19:48 +02:00
2015-10-04 22:02:05 +02:00
/**
2016-05-23 22:10:05 +02:00
* Compare the extent if containing the specified parity position .
2015-10-04 22:02:05 +02:00
*/
2016-05-23 22:10:05 +02:00
static int extent_parity_inside_compare_unlock ( const void * void_a , const void * void_b )
2015-10-04 22:02:05 +02:00
{
2016-05-23 22:10:05 +02:00
const struct extent_parity_inside * arg_a = void_a ;
const struct snapraid_extent * arg_b = void_b ;
2015-10-04 22:02:05 +02:00
if ( arg_a - > parity_pos < arg_b - > parity_pos )
return - 1 ;
2026-06-03 19:00:24 +02:00
if ( arg_a - > parity_pos - arg_b - > parity_pos > = arg_b - > count )
2015-10-04 22:02:05 +02:00
return 1 ;
return 0 ;
2015-10-01 18:19:48 +02:00
}
2015-10-05 21:40:44 +02:00
/**
2016-05-23 22:10:05 +02:00
* Search the extent at the specified parity position .
2015-10-05 21:40:44 +02:00
* The search is optimized for sequential accesses .
* \ return If not found return 0
*/
2016-05-23 22:10:05 +02:00
static struct snapraid_extent * fs_par2extent_get_unlock ( struct snapraid_disk * disk , struct snapraid_extent * * fs_last , block_off_t parity_pos )
2015-10-01 18:19:48 +02:00
{
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent ;
2015-10-04 23:47:06 +02:00
2016-05-23 22:10:05 +02:00
/* check if the last accessed extent matches */
2015-10-30 19:06:24 +01:00
if ( * fs_last
& & parity_pos > = ( * fs_last ) - > parity_pos
2026-06-03 19:00:24 +02:00
& & parity_pos - ( * fs_last ) - > parity_pos < ( * fs_last ) - > count
2015-10-04 23:47:06 +02:00
) {
2016-05-23 22:10:05 +02:00
extent = * fs_last ;
2015-10-04 23:47:06 +02:00
} else {
2016-05-23 22:10:05 +02:00
struct extent_parity_inside arg = { parity_pos } ;
extent = tommy_tree_search_compare ( & disk - > fs_parity , extent_parity_inside_compare_unlock , & arg ) ;
2015-10-04 23:47:06 +02:00
}
2015-10-04 22:02:05 +02:00
2016-05-23 22:10:05 +02:00
if ( ! extent )
2015-10-04 22:02:05 +02:00
return 0 ;
2016-05-23 22:10:05 +02:00
/* store the last accessed extent */
* fs_last = extent ;
2015-10-04 22:02:05 +02:00
2016-05-23 22:10:05 +02:00
return extent ;
2015-10-01 18:19:48 +02:00
}
2016-05-23 22:10:05 +02:00
struct extent_file_inside {
2015-10-04 22:02:05 +02:00
struct snapraid_file * file ;
block_off_t file_pos ;
} ;
/**
2016-05-23 22:10:05 +02:00
* Compare the extent if containing the specified file position .
2015-10-04 22:02:05 +02:00
*/
2016-05-23 22:10:05 +02:00
static int extent_file_inside_compare_unlock ( const void * void_a , const void * void_b )
2015-10-01 18:19:48 +02:00
{
2016-05-23 22:10:05 +02:00
const struct extent_file_inside * arg_a = void_a ;
const struct snapraid_extent * arg_b = void_b ;
2026-08-17 10:07:41 +02:00
uintptr_t file_a = ( uintptr_t ) arg_a - > file ;
uintptr_t file_b = ( uintptr_t ) arg_b - > file ;
2015-10-04 22:02:05 +02:00
2026-08-17 10:07:41 +02:00
/* cast to uintptr_t to avoid undefined behavior when comparing pointers to distinct heap allocations */
if ( file_a < file_b )
2015-10-04 22:02:05 +02:00
return - 1 ;
2026-08-17 10:07:41 +02:00
if ( file_a > file_b )
2015-10-04 22:02:05 +02:00
return 1 ;
if ( arg_a - > file_pos < arg_b - > file_pos )
return - 1 ;
2026-06-03 19:00:24 +02:00
if ( arg_a - > file_pos - arg_b - > file_pos > = arg_b - > count )
2015-10-04 22:02:05 +02:00
return 1 ;
return 0 ;
2015-10-01 18:19:48 +02:00
}
2015-10-05 21:40:44 +02:00
/**
2016-05-23 22:10:05 +02:00
* Search the extent at the specified file position .
2015-10-05 21:40:44 +02:00
* The search is optimized for sequential accesses .
* \ return If not found return 0
*/
2016-05-23 22:10:05 +02:00
static struct snapraid_extent * fs_file2extent_get_unlock ( struct snapraid_disk * disk , struct snapraid_extent * * fs_last , struct snapraid_file * file , block_off_t file_pos )
2015-10-04 23:47:06 +02:00
{
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent ;
2015-10-04 23:47:06 +02:00
2016-05-23 22:10:05 +02:00
/* check if the last accessed extent matches */
2015-10-30 19:06:24 +01:00
if ( * fs_last
& & file = = ( * fs_last ) - > file
& & file_pos > = ( * fs_last ) - > file_pos
2026-06-03 19:00:24 +02:00
& & file_pos - ( * fs_last ) - > file_pos < ( * fs_last ) - > count
2015-10-04 23:47:06 +02:00
) {
2016-05-23 22:10:05 +02:00
extent = * fs_last ;
2015-10-04 23:47:06 +02:00
} else {
2016-05-23 22:10:05 +02:00
struct extent_file_inside arg = { file , file_pos } ;
extent = tommy_tree_search_compare ( & disk - > fs_file , extent_file_inside_compare_unlock , & arg ) ;
2015-10-04 23:47:06 +02:00
}
2016-05-23 22:10:05 +02:00
if ( ! extent )
2015-10-04 23:47:06 +02:00
return 0 ;
2016-05-23 22:10:05 +02:00
/* store the last accessed extent */
* fs_last = extent ;
2015-10-04 23:47:06 +02:00
2016-05-23 22:10:05 +02:00
return extent ;
2015-10-04 23:47:06 +02:00
}
2016-01-16 14:26:05 +01:00
struct snapraid_file * fs_par2file_find ( struct snapraid_disk * disk , block_off_t parity_pos , block_off_t * file_pos )
2015-10-04 23:47:06 +02:00
{
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent ;
2016-01-01 10:00:18 +01:00
struct snapraid_file * file ;
2015-10-04 23:47:06 +02:00
2016-01-01 10:00:18 +01:00
fs_lock ( disk ) ;
2016-05-23 22:10:05 +02:00
extent = fs_par2extent_get_unlock ( disk , & disk - > fs_last , parity_pos ) ;
2016-01-01 10:00:18 +01:00
2016-05-23 22:10:05 +02:00
if ( ! extent ) {
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 23:47:06 +02:00
return 0 ;
2016-01-01 10:00:18 +01:00
}
2015-10-04 23:47:06 +02:00
if ( file_pos )
2016-05-23 22:10:05 +02:00
* file_pos = extent - > file_pos + ( parity_pos - extent - > parity_pos ) ;
2015-10-04 23:47:06 +02:00
2016-05-23 22:10:05 +02:00
file = extent - > file ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
return file ;
2015-10-04 23:47:06 +02:00
}
2016-01-16 14:26:05 +01:00
block_off_t fs_file2par_find ( struct snapraid_disk * disk , struct snapraid_file * file , block_off_t file_pos )
2015-10-01 18:19:48 +02:00
{
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent ;
2016-01-01 10:00:18 +01:00
block_off_t ret ;
2015-10-04 22:02:05 +02:00
2016-01-01 10:00:18 +01:00
fs_lock ( disk ) ;
2016-05-23 22:10:05 +02:00
extent = fs_file2extent_get_unlock ( disk , & disk - > fs_last , file , file_pos ) ;
if ( ! extent ) {
2016-01-15 20:16:06 +01:00
fs_unlock ( disk ) ;
2016-01-16 14:26:05 +01:00
return POS_NULL ;
2015-10-04 22:02:05 +02:00
}
2016-05-23 22:10:05 +02:00
ret = extent - > parity_pos + ( file_pos - extent - > file_pos ) ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
return ret ;
2015-10-01 18:19:48 +02:00
}
2015-10-04 22:02:05 +02:00
void fs_allocate ( struct snapraid_disk * disk , block_off_t parity_pos , struct snapraid_file * file , block_off_t file_pos )
2013-08-23 22:35:14 +02:00
{
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent ;
struct snapraid_extent * parity_extent ;
struct snapraid_extent * file_extent ;
2013-08-23 22:35:14 +02:00
2016-01-01 10:00:18 +01:00
fs_lock ( disk ) ;
2015-10-04 22:02:05 +02:00
if ( file_pos > 0 ) {
2016-05-23 22:10:05 +02:00
/* search an existing extent for the previous file_pos */
extent = fs_file2extent_get_unlock ( disk , & disk - > fs_last , file , file_pos - 1 ) ;
2015-10-04 22:02:05 +02:00
2016-05-23 22:10:05 +02:00
if ( extent ! = 0 & & parity_pos = = extent - > parity_pos + extent - > count ) {
2026-06-03 19:00:24 +02:00
/* check if the position will go outside the limit */
if ( extent - > parity_pos + extent - > count = = POS_NULL ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Parity position overflow allocating file '%s' at position '% " PRIu64 " /% " PRIu64 " ' with count '% " PRIu64 " ' in disk '%s' \n " , file - > sub , file_pos , file - > blockmax , extent - > count , disk - > name ) ;
2026-06-03 19:00:24 +02:00
os_abort ( ) ;
/* LCOV_EXCL_STOP */
}
2016-05-23 22:10:05 +02:00
/* ensure that we are extending the extent at the end */
if ( file_pos ! = extent - > file_pos + extent - > count ) {
2015-10-04 22:02:05 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Allocating file '%s' at position '% " PRIu64 " /% " PRIu64 " ' in the middle of extent '% " PRIu64 " :% " PRIu64 " ' in disk '%s' \n " , file - > sub , file_pos , file - > blockmax , extent - > file_pos , extent - > count , disk - > name ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-10-04 22:02:05 +02:00
/* LCOV_EXCL_STOP */
}
2016-05-23 22:10:05 +02:00
/* extend the existing extent */
+ + extent - > count ;
2015-10-04 22:02:05 +02:00
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
return ;
2013-08-23 22:35:14 +02:00
}
}
2016-05-23 22:10:05 +02:00
/* a extent doesn't exist, and we have to create a new one */
extent = extent_alloc ( parity_pos , file , file_pos , 1 ) ;
2015-10-04 22:02:05 +02:00
2016-05-23 22:10:05 +02:00
/* insert the extent in the trees */
parity_extent = tommy_tree_insert ( & disk - > fs_parity , & extent - > parity_node , extent ) ;
file_extent = tommy_tree_insert ( & disk - > fs_file , & extent - > file_node , extent ) ;
2015-10-05 21:40:44 +02:00
2016-05-23 22:10:05 +02:00
if ( parity_extent ! = extent | | file_extent ! = extent ) {
2015-10-05 21:40:44 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Allocating file '%s' at position '% " PRIu64 " /% " PRIu64 " ' for existing extent '% " PRIu64 " :% " PRIu64 " ' in disk '%s' \n " , file - > sub , file_pos , file - > blockmax , extent - > file_pos , extent - > count , disk - > name ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-10-05 21:40:44 +02:00
/* LCOV_EXCL_STOP */
}
2015-10-04 23:47:06 +02:00
2016-05-23 22:10:05 +02:00
/* store the last accessed extent */
disk - > fs_last = extent ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
}
void fs_deallocate ( struct snapraid_disk * disk , block_off_t parity_pos )
{
2016-05-23 22:10:05 +02:00
struct snapraid_extent * extent ;
struct snapraid_extent * second_extent ;
struct snapraid_extent * parity_extent ;
struct snapraid_extent * file_extent ;
2015-10-05 22:48:16 +02:00
block_off_t first_count , second_count ;
2015-10-04 22:02:05 +02:00
2016-01-01 10:00:18 +01:00
fs_lock ( disk ) ;
2016-05-23 22:10:05 +02:00
extent = fs_par2extent_get_unlock ( disk , & disk - > fs_last , parity_pos ) ;
if ( ! extent ) {
2015-10-04 22:02:05 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Deallocating parity position '% " PRIu64 " ' for not existing extent in disk '%s' \n " , parity_pos , disk - > name ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-10-04 22:02:05 +02:00
/* LCOV_EXCL_STOP */
}
2016-05-23 22:10:05 +02:00
/* if it's the only block of the extent, delete it */
if ( extent - > count = = 1 ) {
2015-10-04 22:02:05 +02:00
/* remove from the trees */
2016-05-23 22:10:05 +02:00
tommy_tree_remove ( & disk - > fs_parity , extent ) ;
tommy_tree_remove ( & disk - > fs_file , extent ) ;
2015-10-04 22:02:05 +02:00
/* deallocate */
2016-05-23 22:10:05 +02:00
extent_free ( extent ) ;
2015-10-04 23:47:06 +02:00
2016-05-23 22:10:05 +02:00
/* clear the last accessed extent */
2015-10-04 23:47:06 +02:00
disk - > fs_last = 0 ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
return ;
}
2016-05-23 22:10:05 +02:00
/* if it's at the start of the extent, shrink the extent */
if ( parity_pos = = extent - > parity_pos ) {
2026-05-21 13:13:56 +02:00
/*
* Note that modifying parity_pos and file_pos in - place is safe
* because extents never overlap . Shrinking the start of the extent
* keeps the relative sorted order of all nodes completely unchanged ,
* preserving the tree invariants without delete / insert overhead .
*/
2016-05-23 22:10:05 +02:00
+ + extent - > parity_pos ;
+ + extent - > file_pos ;
- - extent - > count ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
return ;
}
2016-05-23 22:10:05 +02:00
/* if it's at the end of the extent, shrink the extent */
if ( parity_pos = = extent - > parity_pos + extent - > count - 1 ) {
- - extent - > count ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-10-04 22:02:05 +02:00
return ;
}
2015-10-05 22:48:16 +02:00
/* otherwise it's in the middle */
2016-05-23 22:10:05 +02:00
first_count = parity_pos - extent - > parity_pos ;
second_count = extent - > count - first_count - 1 ;
2015-10-05 22:48:16 +02:00
2016-05-23 22:10:05 +02:00
/* adjust the first extent */
extent - > count = first_count ;
2015-10-05 22:48:16 +02:00
2016-05-23 22:10:05 +02:00
/* allocate the second extent */
second_extent = extent_alloc ( extent - > parity_pos + first_count + 1 , extent - > file , extent - > file_pos + first_count + 1 , second_count ) ;
2015-10-05 22:48:16 +02:00
2016-05-23 22:10:05 +02:00
/* insert the extent in the trees */
parity_extent = tommy_tree_insert ( & disk - > fs_parity , & second_extent - > parity_node , second_extent ) ;
file_extent = tommy_tree_insert ( & disk - > fs_file , & second_extent - > file_node , second_extent ) ;
2015-10-05 22:48:16 +02:00
2016-05-23 22:10:05 +02:00
if ( parity_extent ! = second_extent | | file_extent ! = second_extent ) {
2015-10-05 22:48:16 +02:00
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Deallocating parity position '% " PRIu64 " ' for splitting extent '% " PRIu64 " :% " PRIu64 " ' in disk '%s' \n " , parity_pos , second_extent - > file_pos , second_extent - > count , disk - > name ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-10-05 22:48:16 +02:00
/* LCOV_EXCL_STOP */
}
2016-05-23 22:10:05 +02:00
/* store the last accessed extent */
disk - > fs_last = second_extent ;
2016-01-01 10:00:18 +01:00
fs_unlock ( disk ) ;
2015-11-18 22:10:50 +01:00
}
struct snapraid_block * fs_file2block_get ( struct snapraid_file * file , block_off_t file_pos )
{
if ( file_pos > = file - > blockmax ) {
/* LCOV_EXCL_START */
2026-06-03 23:06:27 +02:00
log_fatal ( EINTERNAL , " Internal inconsistency: Dereferencing file '%s' at position '% " PRIu64 " /% " PRIu64 " ' \n " , file - > sub , file_pos , file - > blockmax ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-11-18 22:10:50 +01:00
/* LCOV_EXCL_STOP */
}
2016-04-07 21:39:02 +02:00
return file_block ( file , file_pos ) ;
2015-10-04 22:02:05 +02:00
}
2016-01-16 14:26:05 +01:00
struct snapraid_block * fs_par2block_find ( struct snapraid_disk * disk , block_off_t parity_pos )
2015-10-04 22:02:05 +02:00
{
struct snapraid_file * file ;
block_off_t file_pos ;
2016-01-16 14:26:05 +01:00
file = fs_par2file_find ( disk , parity_pos , & file_pos ) ;
2016-01-15 20:22:41 +01:00
if ( file = = 0 )
2016-01-16 14:26:05 +01:00
return BLOCK_NULL ;
2015-10-04 22:02:05 +02:00
return fs_file2block_get ( file , file_pos ) ;
2013-08-23 22:35:14 +02:00
}
2014-11-09 19:14:29 +01:00
struct snapraid_map * map_alloc ( const char * name , unsigned position , block_off_t total_blocks , block_off_t free_blocks , const char * uuid )
2011-10-01 19:18:32 +02:00
{
struct snapraid_map * map ;
map = malloc_nofail ( sizeof ( struct snapraid_map ) ) ;
pathcpy ( map - > name , sizeof ( map - > name ) , name ) ;
map - > position = position ;
2014-11-09 19:14:29 +01:00
map - > total_blocks = total_blocks ;
map - > free_blocks = free_blocks ;
2026-06-28 23:35:44 +02:00
pathcpy ( map - > content_uuid , sizeof ( map - > content_uuid ) , uuid ) ;
2011-10-01 19:18:32 +02:00
return map ;
}
void map_free ( struct snapraid_map * map )
{
free ( map ) ;
}
2026-02-08 22:56:22 +01:00
/****************************************************************************/
/* bucket */
2014-09-27 00:32:39 +02:00
2026-02-08 22:56:22 +01:00
static int bucket_cmp ( const void * void_a , const void * void_b )
{
const struct snapraid_bucket * a = void_a ;
const struct snapraid_bucket * b = void_b ;
if ( a - > time_at < b - > time_at )
2013-06-29 17:53:40 +02:00
return - 1 ;
2026-02-08 22:56:22 +01:00
if ( a - > time_at > b - > time_at )
2013-06-29 17:53:40 +02:00
return 1 ;
return 0 ;
}
2016-04-10 22:44:40 +02:00
2026-02-08 22:56:22 +01:00
void bucket_free ( struct snapraid_bucket * bucket )
{
free ( bucket ) ;
}
void bucket_insert ( tommy_hashdyn * bucket_hash , time_t time_at , block_off_t count , int justsynced )
{
2026-02-18 19:52:57 +01:00
tommy_hash_t hash = tommy_inthash_u64 ( time_at ) ;
2026-02-08 22:56:22 +01:00
tommy_node * i = tommy_hashdyn_bucket ( bucket_hash , hash ) ;
while ( i ) {
struct snapraid_bucket * entry = i - > data ;
if ( entry - > time_at = = time_at ) {
if ( justsynced )
entry - > count_justsynced + = count ;
else
entry - > count_scrubbed + = count ;
return ;
}
i = i - > next ;
}
struct snapraid_bucket * entry = malloc_nofail ( sizeof ( struct snapraid_bucket ) ) ;
entry - > time_at = time_at ;
if ( justsynced ) {
entry - > count_justsynced = count ;
entry - > count_scrubbed = 0 ;
} else {
entry - > count_justsynced = 0 ;
entry - > count_scrubbed = count ;
}
tommy_hashdyn_insert ( bucket_hash , & entry - > node , entry , hash ) ;
}
void bucket_to_list ( tommy_hashdyn * bucket_hash , tommy_list * bucket_list , block_off_t * bucketcount )
{
/* clear previous list */
tommy_list_foreach ( bucket_list , ( tommy_foreach_func * ) bucket_free ) ;
tommy_list_init ( bucket_list ) ;
2026-02-09 15:50:58 +01:00
log_tag ( " list:bucket_begin \n " ) ;
log_tag ( " bucket:count:% " PRIu64 " \n " , ( uint64_t ) tommy_hashdyn_count ( bucket_hash ) ) ;
2026-02-08 22:56:22 +01:00
tommy_hashdyn_to_list ( bucket_hash , bucket_list ) ;
tommy_list_sort ( bucket_list , bucket_cmp ) ;
block_off_t count = 0 ;
for ( tommy_node * i = tommy_list_head ( bucket_list ) ; i ! = 0 ; i = i - > next ) {
struct snapraid_bucket * entry = i - > data ;
count + = entry - > count_scrubbed + entry - > count_justsynced ;
2026-06-03 23:06:27 +02:00
log_tag ( " bucket:entry:% " PRIu64 " :% " PRIu64 " :% " PRIu64 " \n " , ( uint64_t ) entry - > time_at , entry - > count_scrubbed , entry - > count_justsynced ) ;
2026-02-08 22:56:22 +01:00
}
2026-02-09 15:50:58 +01:00
2026-06-03 23:06:27 +02:00
log_tag ( " bucket:block_count:% " PRIu64 " \n " , count ) ;
2026-02-09 15:50:58 +01:00
log_tag ( " list:bucket_end \n " ) ;
2026-02-08 22:56:22 +01:00
* bucketcount = count ;
}
2016-10-11 20:30:12 +02:00
/****************************************************************************/
/* format */
int FMT_MODE = FMT_FILE ;
/**
* Format a file path for poll reference
*/
2026-05-29 11:01:44 +02:00
const char * fmt_poll ( const struct snapraid_disk * disk , const char * str )
2016-10-11 20:30:12 +02:00
{
( void ) disk ;
2026-05-29 11:01:44 +02:00
return esc_shell ( str , esc_buf ( ) ) ;
2016-10-11 20:30:12 +02:00
}
/**
* Format a path name for terminal reference
*/
2026-05-29 11:01:44 +02:00
const char * fmt_term ( const struct snapraid_disk * disk , const char * str )
2016-10-11 20:30:12 +02:00
{
const char * out [ 3 ] ;
switch ( FMT_MODE ) {
case FMT_FILE :
default :
2026-05-29 11:01:44 +02:00
return esc_shell ( str , esc_buf ( ) ) ;
2016-10-11 20:30:12 +02:00
case FMT_DISK :
out [ 0 ] = disk - > name ;
out [ 1 ] = " : " ;
out [ 2 ] = str ;
2026-05-29 11:01:44 +02:00
return esc_shell_multi ( out , 3 , esc_buf ( ) ) ;
2016-10-11 20:30:12 +02:00
case FMT_PATH :
2026-04-21 15:19:25 +02:00
out [ 0 ] = disk - > mount_point ;
2016-10-11 20:30:12 +02:00
out [ 1 ] = str ;
2026-05-29 11:01:44 +02:00
return esc_shell_multi ( out , 2 , esc_buf ( ) ) ;
2016-10-11 20:30:12 +02:00
}
}
2026-02-02 11:19:51 +01:00
const char * fmt_size ( uint64_t size , char * buffer , size_t buffer_size )
{
2026-06-03 17:12:23 +02:00
if ( size > = 10ULL * TERA )
2026-02-02 11:19:51 +01:00
snprintf ( buffer , buffer_size , " % " PRIu64 " T " , size / TERA ) ;
2026-06-03 17:12:23 +02:00
else if ( size > = 10ULL * GIGA )
2026-02-02 11:19:51 +01:00
snprintf ( buffer , buffer_size , " % " PRIu64 " G " , size / GIGA ) ;
2026-06-03 17:12:23 +02:00
else if ( size > = 10 * MEGA )
2026-02-02 11:19:51 +01:00
snprintf ( buffer , buffer_size , " % " PRIu64 " M " , size / MEGA ) ;
2026-06-03 17:12:23 +02:00
else if ( size > = 10 * KILO )
2026-02-02 11:19:51 +01:00
snprintf ( buffer , buffer_size , " % " PRIu64 " k " , size / KILO ) ;
else
snprintf ( buffer , buffer_size , " % " PRIu64 " " , size ) ;
return buffer ;
}