1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
|
/*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ELEM_H
#define __ELEM_H
#include "util.h"
#include "support.h"
#include "tommyds/tommyhash.h"
#include "tommyds/tommylist.h"
#include "tommyds/tommytree.h"
#include "tommyds/tommyhashdyn.h"
#include "tommyds/tommyarray.h"
#include "tommyds/tommyarrayblkof.h"
/****************************************************************************/
/* snapraid */
/**
* Number of measures of the operation progress.
*/
#define PROGRESS_MAX 100
/**
* Max UUID length.
*/
#define UUID_MAX 128
/**
* Invalid position.
*/
#define POS_NULL ((block_off_t)-1)
/**
* Content file specification.
*/
struct snapraid_content {
char content[PATH_MAX]; /**< Path of the content file. */
uint64_t device; /**< Device identifier. */
void* context; /**< Context used for multithread operations. */
tommy_node node; /**< Next node in the list. */
};
/**
* Filter for paths.
*/
struct snapraid_filter {
char pattern[PATH_MAX]; /**< Filter pattern. */
int is_disk; /**< If the pattern is a disk one. */
int is_path; /**< If the pattern is only for the complete path. */
int is_dir; /**< If the pattern is only for dir. */
int direction; /**< If it's an inclusion (=1) or an exclusion (=-1). */
tommy_node node; /**< Next node in the list. */
};
/**
* Block pointer used to represent unused blocks.
*/
#define BLOCK_NULL 0
/**
* This block is an empty one.
* Note that an empty block is represent with ::BLOCK_NULL.
*/
#define BLOCK_STATE_EMPTY 0
/**
* The block has both the hash and the parity computed.
* This is the normal state of a saved block.
*
* The block hash field IS set.
* The parity for this disk is updated.
*/
#define BLOCK_STATE_BLK 1
/**
* The block is new and not yet hashed.
* This happens when a new block overwrite a just removed block, or an empty space.
*
* The block hash field MAY be set and it represents the hash of the OLD data.
* The hash may be also INVALID or ZERO.
*
* If the OLD block was empty, the hash is set to the special ZERO value.
* If the OLD block was lost, the hash is set to the special INVALID value.
*
* The parity for this disk is not updated, but it contains the old data referenced by the hash.
*
* If the state is read from an incomplete sync, we don't really know if the hash is referring at the
* data used to compute the parity, because the sync process was interrupted at an unknown point,
* and the parity may or may not be updated.
*
* For this reason we clear all such hashes when reading the state from an incomplete sync before
* starting a new sync, because sync is affected by such hashes, as sync updates the parity, only
* if the new data read for CHG blocks has a mismatching hash.
* Clearing is done setting the ::clear_past_hash flag before reading the state.
* No clearing is done in other commands, as check and fix are instead able to work with unsynced
* hashes, and scrub ignores CHG/DELETED blocks.
*/
#define BLOCK_STATE_CHG 2
/**
* The block is new and hashed.
* This happens when a new block overwrite a just removed block, or an empty space.
*
* Note that when the file copy heuristic is enabled, the REP blocks may be set
* using this heuristic, meaning that the hash may be wrong.
*
* For this reason, when the ::force_nocopy flag is enabled in sync, we convert all the REP blocks
* to CHG, invalidating the stored hash.
* Clearing is done setting the ::clear_past_hash flag before reading the state.
* No clearing is done in other commands, as they don't stop the process like in sync
* when there is a false silent error.
*
* The block hash field IS set, and it represents the hash of the new data.
* The parity for this disk is not updated.
*/
#define BLOCK_STATE_REP 3
/**
* This block is a deleted one.
* This happens when a file is deleted.
*
* The block hash field IS set, and it represents the hash of the previous data,
* but only if it's different by all 0.
* The parity for this disk is not updated, but it contains the old data referenced by the hash.
*
* If the state is read from an incomplete sync, we don't really know if the hash is referring at the
* data used to compute the parity, because the sync process was interrupted at an unknown point,
* and the parity may or may not be updated.
*
* A now the sync process is not affected by DELETED hash, so clearing won't be really needed,
* but considering that we have to do it for CHG blocks, we do it also for DELETED ones,
* clearing all the past hashes.
* Clearing is done setting the ::clear_past_hash flag before reading the state.
* No clearing is done in other commands, as check and fix are instead able to work with unsynced
* hashes, and scrub ignores CHG/DELETED blocks.
*/
#define BLOCK_STATE_DELETED 4
/**
* Block hash size.
*
* At max HASH_MAX.
*/
extern int BLOCK_HASH_SIZE;
/**
* Block of a file.
*/
struct snapraid_block {
unsigned char state; /**< State of the block. */
/**
* Hash of the block.
*
* The effective stored size is BLOCK_HASH_SIZE.
*/
unsigned char hash[HASH_MAX];
};
/**
* If a file is present in the disk.
* It's used only in scan to detect present and missing files.
*/
#define FILE_IS_PRESENT 0x01
/**
* If it's an excluded file from the processing.
* It's used in both check and fix to mark files to exclude from the processing.
*/
#define FILE_IS_EXCLUDED 0x02
/**
* If a fix was attempted but it failed.
* It's used only in fix to mark that some data is unrecoverable.
*/
#define FILE_IS_DAMAGED 0x04
/**
* If a fix was done.
* It's used only in fix to mark that some data was recovered.
*/
#define FILE_IS_FIXED 0x08
/**
* If the file was originally missing, and it was created in the fix process.
* It's used only in fix to mark files recovered from scratch,
* meaning that they don't have any previous content.
* This is important because it means that deleting them, you are not going
* to lose something that cannot be recovered.
* Note that excluded files won't ever get this flag.
*/
#define FILE_IS_CREATED 0x10
/**
* If the file has completed its processing, meaning that it won't be opened anymore.
* It's used only in fix to mark when we finish processing one file.
* Note that excluded files won't ever get this flag.
*/
#define FILE_IS_FINISHED 0x20
/**
* If the file hash was obtained from a file copy
* identified by the same name, size and stamp.
*/
#define FILE_IS_COPY 0x40
/**
* If the file was opened.
* It's used in fix to detect if it's the first time a file is opened.
*/
#define FILE_IS_OPENED 0x80
/**
* If the file is modified from the latest sync.
* It's used in fix to store if the state of the file before being modified.
*/
#define FILE_IS_UNSYNCED 0x100
/**
* If the file is without inode.
* It could happen in file-system where inodes are not persistent,
* or when restoring a full disk with "fix".
* In such cases we have to clear any stored duplicate inode.
* After the scan process completes, no file should have this flag set.
*/
#define FILE_IS_WITHOUT_INODE 0x200
/**
* The file is deleted.
* This happens when a file is deleted from the array,
* but it's keep inside the parity until the next sync.
*
* During the file-system check we needs this information,
* because deleted files may be present only partially.
*/
#define FILE_IS_DELETED 0x400
/**
* The file is missing.
* This happens in fix/check when a file is cannot be opened,
* and marking it as such prevents to retry to open it again.
*/
#define FILE_IS_MISSING 0x800
#define FILE_IS_HARDLINK 0x1000 /**< If it's an hardlink. */
#define FILE_IS_SYMLINK 0x2000 /**< If it's a file symlink. */
#define FILE_IS_SYMDIR 0x4000 /**< If it's a dir symlink for Windows. Not yet supported. */
#define FILE_IS_JUNCTION 0x8000 /**< If it's a junction for Windows. Not yet supported. */
#define FILE_IS_LINK_MASK 0xF000 /**< Mask for link type. */
/**
* File.
*/
struct snapraid_file {
int64_t mtime_sec; /**< Modification time. */
uint64_t inode; /**< Inode. */
uint64_t physical; /**< Physical offset of the file. */
data_off_t size; /**< Size of the file. */
struct snapraid_block* blockvec; /**< All the blocks of the file. */
int mtime_nsec; /**< Modification time nanoseconds. In the range 0 <= x < 1,000,000,000, or STAT_NSEC_INVALID if not present. */
block_off_t blockmax; /**< Number of blocks. */
unsigned flag; /**< FILE_IS_* flags. */
char* sub; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
/* nodes for data structures */
tommy_node nodelist;
tommy_hashdyn_node nodeset;
tommy_hashdyn_node pathset;
tommy_hashdyn_node stampset;
};
/**
* Symbolic Link.
*/
struct snapraid_link {
unsigned flag; /**< FILE_IS_* flags. */
char* sub; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
char* linkto; /**< Link to. */
/* nodes for data structures */
tommy_node nodelist;
tommy_hashdyn_node nodeset;
};
/**
* Dir.
*/
struct snapraid_dir {
unsigned flag; /**< FILE_IS_* flags. */
char* sub; /**< Sub path of the file. Without the disk dir. The disk is implicit. */
/* nodes for data structures */
tommy_node nodelist;
tommy_hashdyn_node nodeset;
};
/**
* Chunk.
*
* A extent represents a fragment of a file mapped into the parity.
*/
struct snapraid_extent {
struct snapraid_file* file; /**< File containing this extent. */
block_off_t parity_pos; /**< Parity position. */
block_off_t file_pos; /**< Position in the file. */
block_off_t count; /**< Number of sequential blocks in the file and parity. */
tommy_tree_node parity_node; /**< Tree sorted by <parity_pos>. */
tommy_tree_node file_node; /**< Tree sorter by <file,file_pos>. */
};
/**
* Disk.
*/
struct snapraid_disk {
char name[PATH_MAX]; /**< Name of the disk. */
char dir[PATH_MAX]; /**< Mount point of the disk. It always terminates with /. */
char smartctl[PATH_MAX]; /**< Custom command for smartctl. Empty means auto. */
int smartignore[SMART_IGNORE_MAX]; /**< Smart attributes to ignore for this device. */
char uuid[UUID_MAX]; /**< UUID of the disk. */
uint64_t device; /**< Device identifier. */
block_off_t total_blocks; /**< Number of total blocks. */
block_off_t free_blocks; /**< Number of free blocks at the last sync. */
uint64_t tick; /**< Usage time. */
uint64_t progress_tick[PROGRESS_MAX]; /**< Last ticks of progress. */
unsigned cached_blocks; /**< Number of IO blocks cached. */
struct snapraid_file* progress_file; /**< File in progress. */
/**
* First free searching block.
* Note that it doesn't necessarily point at the first free block,
* but it just tell you that no free block is present before this position.
*/
block_off_t first_free_block;
int has_volatile_inodes; /**< If the underline file-system has not persistent inodes. */
int has_volatile_hardlinks; /**< If the underline file-system has not synchronized metadata for hardlink (NTFS). */
int has_unreliable_physical; /**< If the physical offset of files has duplicates. */
int has_different_uuid; /**< If the disk has a different UUID, meaning that it is not the same file-system. */
int has_unsupported_uuid; /**< If the disk doesn't report UUID, meaning it's not supported. */
int had_empty_uuid; /**< If the disk had an empty UUID, meaning that it's a new disk. */
int mapping_idx; /**< Index in the mapping vector. Used only as buffer when writing the content file. */
int skip_access; /**< If the disk is inaccessible and it should be skipped. */
#if HAVE_THREAD
/**
* Mutex for protecting the filesystem structure.
*
* Specifically, this protects ::fs_parity, ::fs_file, and ::fs_last,
* meaning that it protects only extents.
*
* Files, links and dirs are not protected as they are not expected to
* change during multithread processing.
*/
thread_mutex_t fs_mutex;
int fs_mutex_enabled; /*< If the lock has to be used. */
/**
* Mutex for protecting the scan process.
*
* It's used during the scan process to protect the stampset to identity copy of files
*/
thread_mutex_t stamp_mutex;
#endif
/**
* Mapping of extents in the parity.
* Sorted by <parity_pos> and by <file,file_pos>
*/
tommy_tree fs_parity;
tommy_tree fs_file;
/**
* Last extent we accessed.
* It's used to optimize access of sequential blocks.
*/
struct snapraid_extent* fs_last;
/**
* List of all the snapraid_file for the disk.
*/
tommy_list filelist;
/**
* List of all the deleted file for the disk.
*
* These files are kept allocated, because the blocks are still referenced in
* the ::blockarr.
*/
tommy_list deletedlist;
tommy_hashdyn inodeset; /**< Hashtable by inode of all the files. */
tommy_hashdyn pathset; /**< Hashtable by path of all the files. */
tommy_hashdyn stampset; /**< Hashtable by stamp (size and time) of all the files. */
tommy_list linklist; /**< List of all the links. */
tommy_hashdyn linkset; /**< Hashtable by name of all the links. */
tommy_list dirlist; /**< List of all the empty dirs. */
tommy_hashdyn dirset; /**< Hashtable by name of all the empty dirs. */
/* nodes for data structures */
tommy_node node;
};
/**
* Disk mapping.
*/
struct snapraid_map {
char name[PATH_MAX]; /**< Name of the disk. */
char uuid[UUID_MAX]; /**< UUID of the disk. Empty if unknown. */
block_off_t total_blocks; /**< Number of total blocks. */
block_off_t free_blocks; /**< Number of free blocks at last 'sync'. */
unsigned position; /**< Position of the disk in the parity. */
/* nodes for data structures */
tommy_node node;
};
/**
* Max number of parity split.
*/
#define SPLIT_MAX 8
/**
* Invalid parity size.
*
* This value is used to identify new parities,
* like when you alter the configuration adding
* a new parity level, creating it with 'fix'.
* Given that 'fix' doesn't write the content file,
* the new size will be written only at the next
* 'sync'.
*/
#define PARITY_SIZE_INVALID -1LL
/**
* Parity split.
*/
struct snapraid_split {
char path[PATH_MAX]; /**< Path of the parity file. */
char uuid[UUID_MAX]; /**< UUID of the disk. Empty if unknown. */
/**
* Size of the parity split.
* Only the latest not zero size is allowed to grow.
* If the value is unset, it's PARITY_SIZE_INVALID.
*/
data_off_t size;
uint64_t device; /**< Device identifier of the parity. */
};
/**
* Parity.
*/
struct snapraid_parity {
struct snapraid_split split_map[SPLIT_MAX]; /**< Parity splits. */
unsigned split_mac; /**< Number of parity splits. */
char smartctl[PATH_MAX]; /**< Custom command for smartctl. Empty means auto. */
int smartignore[SMART_IGNORE_MAX]; /**< Smart attributes to ignore for this device. */
block_off_t total_blocks; /**< Number of total blocks. */
block_off_t free_blocks; /**< Number of free blocks at the last sync. */
int is_excluded_by_filter; /**< If the parity is excluded by filters. */
int skip_access; /**< If at least one of the parity disk is inaccessible and it should be skipped. */
uint64_t tick; /**< Usage time. */
uint64_t progress_tick[PROGRESS_MAX]; /**< Last cpu ticks of progress. */
unsigned cached_blocks; /**< Number of IO blocks cached. */
};
/**
* Info.
*/
typedef uint64_t snapraid_info;
/**
* Allocate a content.
*/
struct snapraid_content* content_alloc(const char* path, uint64_t dev);
/**
* Deallocate a content.
*/
void content_free(struct snapraid_content* content);
/**
* Allocate a filter pattern for files and directories.
*/
struct snapraid_filter* filter_alloc_file(int is_include, const char* pattern);
/**
* Allocate a filter pattern for disks.
*/
struct snapraid_filter* filter_alloc_disk(int is_include, const char* pattern);
/**
* Deallocate an exclusion.
*/
void filter_free(struct snapraid_filter* filter);
/**
* Filter type description.
*/
const char* filter_type(struct snapraid_filter* filter, char* out, size_t out_size);
/**
* Filter hidden files.
* Return !=0 if it matches and it should be excluded.
*/
static inline int filter_hidden(int enable, struct dirent* dd)
{
if (enable && dirent_hidden(dd)) {
return 1; /* filter out */
}
return 0;
}
/**
* Filter a path using a list of filters.
* For each element of the path all the filters are applied, until the first one that matches.
* Return !=0 if it should be excluded.
*/
int filter_path(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub);
/**
* Filter a file/link/dir if missing.
* This call imply a disk check for the file presence.
* Return !=0 if the file is present and it should be excluded.
*/
int filter_existence(int filter_missing, const char* dir, const char* sub);
/**
* Filter a file if bad.
* Return !=0 if the file is correct and it should be excluded.
*/
int filter_correctness(int filter_error, tommy_arrayblkof* infoarr, struct snapraid_disk* disk, struct snapraid_file* file);
/**
* Filter a dir using a list of filters.
* For each element of the path all the filters are applied, until the first one that matches.
* Thesesdir are always by included by default, to allow to apply rules at the contained files.
* Return !=0 if should be excluded.
*/
int filter_subdir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub);
/**
* Filter a dir using a list of filters.
* For each element of the path all the filters are applied, until the first one that matches.
* Return !=0 if should be excluded.
*/
int filter_emptydir(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub);
/**
* Filter a path if it's a content file.
* Return !=0 if should be excluded.
*/
int filter_content(tommy_list* contentlist, const char* path);
/**
* Check if the specified hash is invalid.
*
* An invalid hash is represented with all bytes at 0x00.
*
* If working with reduced hash lengths, this function always return 0.
*/
static inline int hash_is_invalid(const unsigned char* hash)
{
int i;
/* if the hash is reduced, we cannot grant that it's a specific kind of hash */
if (BLOCK_HASH_SIZE != HASH_MAX)
return 0;
for (i = 0; i < BLOCK_HASH_SIZE; ++i)
if (hash[i] != 0x00)
return 0;
return 1;
}
/**
* Check if the specified hash represent the zero block.
*
* A zero hash is represented with all bytes at 0xFF.
*
* If working with reduced hash lengths, this function always return 0.
*/
static inline int hash_is_zero(const unsigned char* hash)
{
int i;
/* if the hash is reduced, we cannot grant that it's a specific kind of hash */
if (BLOCK_HASH_SIZE != HASH_MAX)
return 0;
for (i = 0; i < BLOCK_HASH_SIZE; ++i)
if (hash[i] != 0xFF)
return 0;
return 1;
}
/**
* Check if the specified hash is unequivocally representing the data.
*
* If working with reduced hash lengths, this function always return 0.
*/
static inline int hash_is_unique(const unsigned char* hash)
{
/* if the hash is reduced, we cannot grant that it's a specific kind of hash */
if (BLOCK_HASH_SIZE != HASH_MAX)
return 0;
return !hash_is_zero(hash) && !hash_is_invalid(hash);
}
/**
* Set the hash to the special INVALID value.
*/
static inline void hash_invalid_set(unsigned char* hash)
{
memset(hash, 0x00, BLOCK_HASH_SIZE);
}
/**
* Set the hash to the special ZERO value.
*/
static inline void hash_zero_set(unsigned char* hash)
{
memset(hash, 0xFF, BLOCK_HASH_SIZE);
}
/**
* Allocated space for block.
*/
static inline size_t block_sizeof(void)
{
return 1 + BLOCK_HASH_SIZE;
}
/**
* Get the state of the block.
*
* For this function, it's allowed to pass a NULL block
* pointer than results in the BLOCK_STATE_EMPTY state.
*/
static inline unsigned block_state_get(const struct snapraid_block* block)
{
if (block == BLOCK_NULL)
return BLOCK_STATE_EMPTY;
return block->state;
}
/**
* Set the state of the block.
*/
static inline void block_state_set(struct snapraid_block* block, unsigned state)
{
block->state = state;
}
/**
* Check if the specified block has an updated hash.
*
* Note that EMPTY / CHG / DELETED return 0.
*/
static inline int block_has_updated_hash(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_BLK || state == BLOCK_STATE_REP;
}
/**
* Check if the specified block has a past hash,
* i.e. the hash of the data that it's now overwritten or lost.
*
* Note that EMPTY / BLK / REP return 0.
*/
static inline int block_has_past_hash(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_CHG || state == BLOCK_STATE_DELETED;
}
/**
* Check if the specified block is part of a file.
*
* Note that EMPTY / DELETED return 0.
*/
static inline int block_has_file(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_BLK
|| state == BLOCK_STATE_CHG || state == BLOCK_STATE_REP;
}
/**
* Check if the block has an invalid parity than needs to be updated.
*
* Note that EMPTY / BLK return 0.
*/
static inline int block_has_invalid_parity(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_DELETED
|| state == BLOCK_STATE_CHG || state == BLOCK_STATE_REP;
}
/**
* Check if the block is part of a file with valid parity.
*
* Note that anything different than BLK return 0.
*/
static inline int block_has_file_and_valid_parity(const struct snapraid_block* block)
{
unsigned state = block_state_get(block);
return state == BLOCK_STATE_BLK;
}
static inline int file_flag_has(const struct snapraid_file* file, unsigned mask)
{
return (file->flag & mask) == mask;
}
static inline void file_flag_set(struct snapraid_file* file, unsigned mask)
{
file->flag |= mask;
}
static inline void file_flag_clear(struct snapraid_file* file, unsigned mask)
{
file->flag &= ~mask;
}
/**
* Allocate a file.
*/
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);
/**
* Duplicate a file.
*/
struct snapraid_file* file_dup(struct snapraid_file* copy);
/**
* Deallocate a file.
*/
void file_free(struct snapraid_file* file);
/**
* Rename a file.
*/
void file_rename(struct snapraid_file* file, const char* sub);
/**
* Copy a file.
*/
void file_copy(struct snapraid_file* src_file, struct snapraid_file* dest_file);
/**
* Return the block at the specified position.
*
* Note that the block size if a runtime value.
*/
static inline struct snapraid_block* file_block(struct snapraid_file* file, size_t pos)
{
unsigned char* ptr = (unsigned char*)file->blockvec;
return (struct snapraid_block*)(ptr + pos * block_sizeof());
}
/**
* Return the name of the file, without the dir.
*/
const char* file_name(const struct snapraid_file* file);
/**
* Check if the block is the last in the file.
*/
int file_block_is_last(struct snapraid_file* file, block_off_t file_pos);
/**
* Get the size in bytes of the block.
* If it's the last block of a file it could be less than block_size.
*/
unsigned file_block_size(struct snapraid_file* file, block_off_t file_pos, unsigned block_size);
/**
* Compare a file with an inode.
*/
int file_inode_compare_to_arg(const void* void_arg, const void* void_data);
/**
* Compare files by inode.
*/
int file_inode_compare(const void* void_a, const void* void_b);
/**
* Compare files by path.
*/
int file_path_compare(const void* void_a, const void* void_b);
/**
* Compare files by physical address.
*/
int file_physical_compare(const void* void_a, const void* void_b);
/**
* Compute the hash of a file inode.
*/
static inline tommy_uint32_t file_inode_hash(uint64_t inode)
{
return (tommy_uint32_t)tommy_inthash_u64(inode);
}
/**
* Compare a file with a path.
*/
int file_path_compare_to_arg(const void* void_arg, const void* void_data);
/**
* Compare a file with another file for name, stamp, and both.
*/
int file_name_compare(const void* void_a, const void* void_b);
int file_stamp_compare(const void* void_a, const void* void_b);
int file_namestamp_compare(const void* void_a, const void* void_b);
int file_pathstamp_compare(const void* void_a, const void* void_b);
/**
* Compute the hash of a file path.
*/
static inline tommy_uint32_t file_path_hash(const char* sub)
{
return tommy_hash_u32(0, sub, strlen(sub));
}
/**
* Compute the hash of a file stamp.
*/
static inline tommy_uint32_t file_stamp_hash(data_off_t size, int64_t mtime_sec, int mtime_nsec)
{
return tommy_inthash_u32((tommy_uint32_t)size ^ tommy_inthash_u32(mtime_sec ^ tommy_inthash_u32(mtime_nsec)));
}
/**
* Allocate a extent.
*/
struct snapraid_extent* extent_alloc(block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos, block_off_t count);
/**
* Deallocate a extent.
*/
void extent_free(struct snapraid_extent* extent);
/**
* Compare extent by parity position.
*/
int extent_parity_compare(const void* void_a, const void* void_b);
/**
* Compare extent by file and file position.
*/
int extent_file_compare(const void* void_a, const void* void_b);
static inline int link_flag_has(const struct snapraid_link* slink, unsigned mask)
{
return (slink->flag & mask) == mask;
}
static inline void link_flag_set(struct snapraid_link* slink, unsigned mask)
{
slink->flag |= mask;
}
static inline void link_flag_clear(struct snapraid_link* slink, unsigned mask)
{
slink->flag &= ~mask;
}
static inline void link_flag_let(struct snapraid_link* slink, unsigned flag, unsigned mask)
{
slink->flag &= ~mask;
slink->flag |= flag & mask;
}
static inline unsigned link_flag_get(struct snapraid_link* slink, unsigned mask)
{
return slink->flag & mask;
}
/**
* Allocate a link.
*/
struct snapraid_link* link_alloc(const char* name, const char* slink, unsigned link_flag);
/**
* Deallocate a link.
*/
void link_free(struct snapraid_link* slink);
/**
* Compare a link with a name.
*/
int link_name_compare_to_arg(const void* void_arg, const void* void_data);
/**
* Compare links by path.
*/
int link_alpha_compare(const void* void_a, const void* void_b);
/**
* Compute the hash of a link name.
*/
static inline tommy_uint32_t link_name_hash(const char* name)
{
return tommy_hash_u32(0, name, strlen(name));
}
static inline int dir_flag_has(const struct snapraid_dir* dir, unsigned mask)
{
return (dir->flag & mask) == mask;
}
static inline void dir_flag_set(struct snapraid_dir* dir, unsigned mask)
{
dir->flag |= mask;
}
static inline void dir_flag_clear(struct snapraid_dir* dir, unsigned mask)
{
dir->flag &= ~mask;
}
/**
* Allocate a dir.
*/
struct snapraid_dir* dir_alloc(const char* name);
/**
* Deallocate a dir.
*/
void dir_free(struct snapraid_dir* dir);
/**
* Compare a dir with a name.
*/
int dir_name_compare(const void* void_arg, const void* void_data);
/**
* Compute the hash of a dir name.
*/
static inline tommy_uint32_t dir_name_hash(const char* name)
{
return tommy_hash_u32(0, name, strlen(name));
}
/**
* Allocate a disk.
*/
struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev, const char* uuid, int skip_access);
/**
* Deallocate a disk.
*/
void disk_free(struct snapraid_disk* disk);
/**
* Enable multithread support for the disk.
*/
void disk_start_thread(struct snapraid_disk* disk);
/**
* Get the size of the disk in blocks.
*/
block_off_t fs_size(struct snapraid_disk* disk);
/**
* Check if a disk is totally empty and can be discarded from the content file.
* A disk is empty if it doesn't contain any file, symlink, hardlink or dir
* and without any DELETED block.
* The blockmax is used to limit the search of DELETED block up to blockmax.
*/
int fs_is_empty(struct snapraid_disk* disk, block_off_t blockmax);
/**
* Check the file-system for errors.
* Return 0 if it's OK.
*/
int fs_check(struct snapraid_disk* disk);
/**
* Allocate a parity position for the specified file position.
*
* After this call you can use the par2file/par2block operations
* to query the relation.
*
* \note This function is NOT thread-safe as it uses the disk cache. +
*/
void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snapraid_file* file, block_off_t file_pos);
/**
* Deallocate the parity position.
*
* After this call the par2file/par2block operations
* won't find anymore the parity association.
*
* \note This function is NOT thread-safe as it uses the disk cache.
*/
void fs_deallocate(struct snapraid_disk* disk, block_off_t pos);
/**
* Get the block from the file position.
*/
struct snapraid_block* fs_file2block_get(struct snapraid_file* file, block_off_t file_pos);
/**
* Get the file position from the parity position.
* Return 0 if no file is using it.
*/
struct snapraid_file* fs_par2file_find(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos);
/**
* Get the file position from the parity position.
*/
static inline struct snapraid_file* fs_par2file_get(struct snapraid_disk* disk, block_off_t parity_pos, block_off_t* file_pos)
{
struct snapraid_file* ret;
ret = fs_par2file_find(disk, parity_pos, file_pos);
if (ret == 0) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency: Deresolving parity to file at position '%u' in disk '%s'\n", parity_pos, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
return ret;
}
/**
* Get the parity position from the file position.
* Return POS_NULL if no parity is allocated.
*/
block_off_t fs_file2par_find(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos);
/**
* Get the parity position from the file position.
*/
static inline block_off_t fs_file2par_get(struct snapraid_disk* disk, struct snapraid_file* file, block_off_t file_pos)
{
block_off_t ret;
ret = fs_file2par_find(disk, file, file_pos);
if (ret == POS_NULL) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency: Resolving file '%s' at position '%u/%u' in disk '%s'\n", file->sub, file_pos, file->blockmax, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
return ret;
}
/**
* Get the block from the parity position.
* Return BLOCK_NULL==0 if the block is over the end of the disk or not used.
*/
struct snapraid_block* fs_par2block_find(struct snapraid_disk* disk, block_off_t parity_pos);
/**
* Get the block from the parity position.
*/
static inline struct snapraid_block* fs_par2block_get(struct snapraid_disk* disk, block_off_t parity_pos)
{
struct snapraid_block* ret;
ret = fs_par2block_find(disk, parity_pos);
if (ret == BLOCK_NULL) {
/* LCOV_EXCL_START */
log_fatal("Internal inconsistency: Deresolving parity to block at position '%u' in disk '%s'\n", parity_pos, disk->name);
os_abort();
/* LCOV_EXCL_STOP */
}
return ret;
}
/**
* Allocate a disk mapping.
* Uses uuid="" if not available.
*/
struct snapraid_map* map_alloc(const char* name, unsigned position, block_off_t total_blocks, block_off_t free_blocks, const char* uuid);
/**
* Deallocate a disk mapping.
*/
void map_free(struct snapraid_map* map);
/**
* Mask used to store additional information in the info bits.
*
* These bits reduce the granularity of the time in the memory representation.
*/
#define INFO_MASK ((snapraid_info)0x7)
/**
* Make an info.
*/
static inline snapraid_info info_make(time_t last_access, int error, int rehash, int justsynced)
{
/* clear the lowest bits as reserved for other information */
snapraid_info info = last_access & ~INFO_MASK;
if (error != 0)
info |= 0x1;
if (rehash != 0)
info |= 0x2;
if (justsynced != 0)
info |= 0x4;
return info;
}
/**
* Extract the time information.
* This is the last time when the block was know to be correct.
* The "scrubbed" info tells if the time is referring at the latest sync or scrub.
*/
static inline time_t info_get_time(snapraid_info info)
{
return info & ~INFO_MASK;
}
/**
* Extract the error information.
* Report if the block address had some problem.
*/
static inline int info_get_bad(snapraid_info info)
{
return (info & 0x1) != 0;
}
/**
* Extract the rehash information.
* Report if the block address is using the old hash and needs to be rehashed.
*/
static inline int info_get_rehash(snapraid_info info)
{
return (info & 0x2) != 0;
}
/**
* Extract the scrubbed information.
* Report if the block address was never scrubbed.
*/
static inline int info_get_justsynced(snapraid_info info)
{
return (info & 0x4) != 0;
}
/**
* Mark the block address as with error.
*/
static inline snapraid_info info_set_bad(snapraid_info info)
{
return info | 0x1;
}
/**
* Mark the block address as with rehash.
*/
static inline snapraid_info info_set_rehash(snapraid_info info)
{
return info | 0x2;
}
/**
* Set the info at the specified position.
* The position is allocated if not yet done.
*/
static inline void info_set(tommy_arrayblkof* array, block_off_t pos, snapraid_info info)
{
tommy_arrayblkof_grow(array, pos + 1);
memcpy(tommy_arrayblkof_ref(array, pos), &info, sizeof(snapraid_info));
}
/**
* Get the info at the specified position.
* For not allocated position, 0 is returned.
*/
static inline snapraid_info info_get(tommy_arrayblkof* array, block_off_t pos)
{
snapraid_info info;
if (pos >= tommy_arrayblkof_size(array))
return 0;
memcpy(&info, tommy_arrayblkof_ref(array, pos), sizeof(snapraid_info));
return info;
}
/**
* Compare times
*/
int time_compare(const void* void_a, const void* void_b);
/****************************************************************************/
/* format */
#define FMT_FILE 0 /**< Print only the file. */
#define FMT_DISK 1 /**< Print the disk name and the file. */
#define FMT_PATH 2 /**< Print the full path. */
extern int FMT_MODE;
/**
* Format a file path for poll reference
*/
const char* fmt_poll(const struct snapraid_disk* disk, const char* str, char* buffer);
/**
* Format a path name for terminal reference
*/
const char* fmt_term(const struct snapraid_disk* disk, const char* str, char* buffer);
#endif
|