File: elem.h

package info (click to toggle)
snapraid 13.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,656 kB
  • sloc: ansic: 46,018; makefile: 1,004; sh: 154
file content (1249 lines) | stat: -rw-r--r-- 34,726 bytes parent folder | download
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