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