snapraid/cmdline/portable.h
Andrea Mazzoleni 20cdfa7535 Remove the requirement to snapshot content files
The new content file format now includes metadata for previous versions
of files. This allows the fix command to access historical file data from
snapshots as needed, ensuring recovery even if the live filesystem has
changed or the file was deleted during a sync.
2026-07-13 11:03:55 +02:00

635 lines
15 KiB
C

// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2011 Andrea Mazzoleni
#ifndef __PORTABLE_H
#define __PORTABLE_H
#if HAVE_CONFIG_H
#include "config.h" /* Use " to include first in the same directory of this file */
#endif
/***************************************************************************/
/* Config */
#ifdef __MINGW32__
/**
* Enable the GNU printf functions instead of using the MSVCRT ones.
*
* Note that this is the default if _POSIX is also defined.
* To disable it you have to set it to 0.
*/
#define __USE_MINGW_ANSI_STDIO 1
/**
* Define the MSVCRT version targeting Windows Vista.
*/
#define __MSVCRT_VERSION__ 0x0600
/**
* Include Windows Vista headers.
*
* Like for InitializeCriticalSection().
*/
#define _WIN32_WINNT 0x600
/**
* Undef as it clashes with windows.h declarations
*/
#undef DATADIR
#include <windows.h>
#endif
/**
* Specify the format attribute for printf.
*/
#ifdef __MINGW32__
#if defined(__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO == 1
#define attribute_printf gnu_printf /* GNU format */
#else
#define attribute_printf ms_printf /* MSVCRT format */
#endif
#else
#define attribute_printf printf /* GNU format is the default one */
#endif
/**
* Compiler extension
*/
#ifndef __always_inline
#define __always_inline inline __attribute__((always_inline))
#endif
#ifndef __noreturn
#define __noreturn __attribute__((noreturn))
#endif
/**
* Architecture for inline assembly.
*/
#if HAVE_ASSEMBLY
#if defined(__i386__)
#define CONFIG_X86 1
#define CONFIG_X86_32 1
#endif
#if defined(__x86_64__)
#define CONFIG_X86 1
#define CONFIG_X86_64 1
#endif
#endif
/**
* Includes some platform specific headers.
*/
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#if HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
#if HAVE_LINUX_BTRFS_H
#include <linux/btrfs.h>
#endif
#if HAVE_LINUX_FIEMAP_H
#include <linux/fiemap.h>
#endif
#if HAVE_BLKID_BLKID_H
#include <blkid/blkid.h>
#if HAVE_BLKID_DEVNO_TO_DEVNAME && HAVE_BLKID_GET_TAG_VALUE
#define HAVE_BLKID 1
#endif
#endif
/**
* Includes some standard headers.
*/
#include <stdio.h>
#include <stdlib.h> /* On many systems (e.g., Darwin), `stdio.h' is a prerequisite. */
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <limits.h>
#if HAVE_STDINT_H
#include <stdint.h>
#endif
#if HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#if HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
#if HAVE_DIRENT_H
#include <dirent.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)
#else
#define dirent direct
#define NAMLEN(dirent) (dirent)->d_namlen
#if HAVE_SYS_NDIR_H
#include <sys/ndir.h>
#endif
#if HAVE_SYS_DIR_H
#include <sys/dir.h>
#endif
#if HAVE_NDIR_H
#include <ndir.h>
#endif
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_MKDEV
#include <sys/mkdev.h>
#endif
#if HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#if HAVE_GETOPT_H
#include <getopt.h>
#endif
#if HAVE_MATH_H
#include <math.h>
#endif
#if HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include <mach-o/dyld.h>
#endif
#if HAVE_IO_H
#include <io.h>
#endif
#if HAVE_GETOPT_LONG
#define SWITCH_GETOPT_LONG(a, b) a
#else
#define SWITCH_GETOPT_LONG(a, b) b
#endif
/**
* Enables lock file support.
*/
#if HAVE_FLOCK && HAVE_FTRUNCATE
#define HAVE_LOCKFILE 1
#endif
/**
* Includes specific support for Windows or Linux.
*/
#ifdef __MINGW32__
#include "mingw.h"
#else
#include "unix.h"
#endif
/****************************************************************************/
/* os */
/**
* Default paths.
*/
void os_default_conf(char* dst, size_t dst_size, const char* argv0);
/**
* Get the os_tick counter value.
*
* Note that the frequency is unspecified, because the time measure
* is meant to be used to compare the ratio between usage times.
*/
uint64_t os_tick(void);
/**
* Get the os_tick counter value in millisecond.
*/
uint64_t os_tick_ms(void);
/**
* Initializes the system.
*/
void os_init(int opt);
/**
* Deinitialize the system.
*/
void os_done(void);
/**
* Abort the process with a stacktrace.
*/
void os_abort(void) __noreturn;
/**
* Clear the screen.
*/
void os_clear(void);
/**
* Global variable to identify if Ctrl+C is pressed.
*/
extern volatile int global_interrupt;
/****************************************************************************/
/* app */
/**
* Include list support to have tommy_node.
*/
#include "tommyds/tommylist.h"
/**
* Basic block position type.
* With 32 bits and 128k blocks you can address 256 TB.
*/
typedef uint32_t block_off_t;
/**
* Basic data position type.
* It's signed as file size and offset are usually signed.
*/
typedef int64_t data_off_t;
/**
* Another name for link() to avoid confusion with local variables called "link".
*/
static inline int hardlink(const char* a, const char* b)
{
return link(a, b);
}
/**
* Get the device UUID.
* Return 0 on success.
*/
int devuuid(uint64_t device_id, const char* device_path, char* uuid, size_t size);
/**
* Physical offset not yet read.
*/
#define FILEPHY_UNREAD_OFFSET 0
/**
* Special value returned when the file-system doesn't report any offset for unknown reason.
*/
#define FILEPHY_UNREPORTED_OFFSET 1
/**
* Special value returned when the file doesn't have a real offset.
* For example, because it's stored in the NTFS MFT.
*/
#define FILEPHY_WITHOUT_OFFSET 2
/**
* Value indicating real offsets. All offsets greater or equal at this one are real.
*/
#define FILEPHY_REAL_OFFSET 3
/**
* Get the physical address of the specified file.
* This is expected to be just a hint and not necessarily correct or unique.
* Return 0 on success.
*/
int filephy(const char* path, uint64_t size, uint64_t* physical);
/**
* Check if the underline file-system support persistent inodes.
* Return -1 on error, 0 on success.
*/
int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_hardlinks, uint64_t* total_space, uint64_t* free_space, char* fstype, size_t fstype_size, char* fslabel, size_t fslabel_size);
/**
* Get the subvolume directory if the underline filesystem support it
* @param path Full path of the mount point to check. It must end with /
* @param root The full path of the subvolume root that contains the specified path. It will end with /.
*/
int fssnapshot(const char* path, char* root, size_t root_size);
/**
* Creates a snapshot of a subvolume.
* @param source Path to the source subvolume.
* @param parent_dir Directory where the snapshot should be created.
* @param name Name of snapshot.
* @return 0 on success, -1 on failure.
*/
int fssnapshot_create(const char* source, const char* parent_dir, const char* name);
/**
* Deletes a subvolume/snapshot.
* @param parent_dir Directory where the snapshot is.
* @param name Name of snapshot.
* @return 0 on success, -1 on failure.
*/
int fssnapshot_delete(const char* parent_dir, const char* name);
/**
* Renames a subvolume.
* @return 0 on success, -1 on failure.
*/
int fssnapshot_rename(const char* parent_dir, const char* old_name, const char* new_name);
/*
* Log file.
*
* This stream if fully buffered.
*
* If no log file is selected, it's 0.
*/
extern FILE* stdlog;
/**
* Exit codes for testing.
*/
extern int exit_success;
extern int exit_failure;
extern int exit_sync_needed;
#undef EXIT_SUCCESS
#undef EXIT_FAILURE
#define EXIT_SUCCESS exit_success
#define EXIT_FAILURE exit_failure
#define EXIT_SYNC_NEEDED exit_sync_needed
/**
* Fill memory with pseudo-random values.
*/
int randomize(void* ptr, size_t size);
/**
* Standard SMART attributes.
*/
#define SMART_REALLOCATED_SECTOR_COUNT 5
#define SMART_POWER_ON_HOURS 9
#define SMART_POWER_CYCLE_COUNT 12
#define SMART_UNCORRECTABLE_ERROR_CNT 187
#define SMART_COMMAND_TIMEOUT 188
#define SMART_CURRENT_PENDING_SECTOR 197
#define SMART_OFFLINE_UNCORRECTABLE 198
#define SMART_START_STOP_COUNT 4
#define SMART_POWER_ON_HOURS 9
#define SMART_AIRFLOW_TEMPERATURE_CELSIUS 190
#define SMART_LOAD_CYCLE_COUNT 193
#define SMART_TEMPERATURE_CELSIUS 194
/**
* Flags returned by smartctl via exit code
*/
#define SMART_FLAGS 256
/*
* Counts command, transport, or controller-level errors reported by the device.
* These reflect failed I/O operations not directly caused by media defects
* (for example interface, firmware, or power-related errors).
* This counter is cumulative and never resets to zero, even if the underlying
* error condition is resolved.
*/
#define SMART_ERROR_PROTOCOL 257
/*
* Counts media-level errors where data could not be reliably read or written.
* These indicate actual storage surface or flash failures and may imply data loss.
* This counter is cumulative and never resets to zero, even if the underlying
* error condition is resolved.
*/
#define SMART_ERROR_MEDIUM 258
/**
* Unified Wear Level Metric (0-100)
*
* Represents the remaining life of an SSD.
* - 0: Brand new drive (0% wear).
* - 100: Drive has reached or exceeded its manufacturer-rated design life.
*/
#define SMART_WEAR_LEVEL 259
/**
* SMART attributes count.
*/
#define SMART_COUNT 260
/**
* NVME custom SMART attributes
*
* The numbers are just arbitrary to put them in unused space
*/
#define SMART_NVME_CRITICAL_WARNING 100
#define SMART_NVME_AVAILABLE_SPARE 101
#define SMART_NVME_DATA_UNITS_READ 102
#define SMART_NVME_DATA_UNITS_WRITTEN 103
#define SMART_NVME_HOST_READ_COMMANDS 104
#define SMART_NVME_HOST_WRITE_COMMANDS 105
#define SMART_NVME_CONTROLLER_BUSY_TIME 106
#define SMART_NVME_UNSAFE_SHUTDOWNS 107
#define SMART_NVME_WARNING_COMP_TEMPERATURE_TIME 108
#define SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME 109
/**
* Info attributes.
*/
#define INFO_SIZE 0 /**< Size in bytes. */
#define INFO_ROTATION_RATE 1 /**< Rotation speed. 0 for SSD. */
/**
* Info attributes count.
*/
#define INFO_COUNT 2
/**
* Max number of ignored smart attributes
*/
#define SMART_IGNORE_MAX 4
/**
* Flags returned by smartctl.
*/
#define SMARTCTL_FLAG_UNSUPPORTED (1 << 0) /**< Device not recognized, requiring the -d option. */
#define SMARTCTL_FLAG_OPEN (1 << 1) /**< Device open or identification failed. */
#define SMARTCTL_FLAG_COMMAND (1 << 2) /**< Some SMART or ATA commands failed. This is a common error, also happening with full info gathering. */
#define SMARTCTL_FLAG_FAIL (1 << 3) /**< SMART status check returned "DISK FAILING". */
#define SMARTCTL_FLAG_PREFAIL (1 << 4) /**< We found prefail Attributes <= threshold. */
#define SMARTCTL_FLAG_PREFAIL_LOGGED (1 << 5) /**< SMART status check returned "DISK OK" but we found that some (usage or prefail) Attributes have been <= threshold at some time in the past. */
#define SMARTCTL_FLAG_ERROR_LOGGED (1 << 6) /**< The device error log contains records of errors. */
#define SMARTCTL_FLAG_SELFERROR_LOGGED (1 << 7) /**< The device self-test log contains records of errors. */
/**
* SMART max attribute length.
*/
#define SMART_MAX 64
/**
* Value for unassigned SMART attribute.
*/
#define SMART_UNASSIGNED 0xFFFFFFFFFFFFFFFFULL
/**
* SMART Attribute flags
*/
#define SMART_ATTR_TYPE_PREFAIL 1
#define SMART_ATTR_TYPE_OLDAGE 2
#define SMART_ATTR_UPDATE_ALWAYS 4
#define SMART_ATTR_UPDATE_OFFLINE 8
#define SMART_ATTR_WHEN_FAILED_NOW 16
#define SMART_ATTR_WHEN_FAILED_PAST 32
#define SMART_ATTR_WHEN_FAILED_NEVER 64
struct smart_attr {
char name[128]; /**< SMART attribute name. */
uint64_t raw; /**< SMART attributes raw. It can be SMART_UNASSIGNED. */
uint64_t norm; /**< SMART attributes normalized. It can be SMART_UNASSIGNED even if raw is not. */
uint64_t worst; /**< SMART attributes worst. It can be SMART_UNASSIGNED even if raw/norm is not. */
uint64_t thresh; /**< SMART attributes threshold. It can be SMART_UNASSIGNED even if raw/norm/worst is not. */
int flags; /**< SMART_ATTR_* flags */
};
/**
* Power mode
*/
#define POWER_STANDBY 0
#define POWER_ACTIVE 1
#define POWER_UNKNOWN -1
/**
* Device info entry.
*/
struct devinfo_struct {
uint64_t device; /**< Device ID. */
char name[PATH_MAX]; /**< Name of the disk combined with the split index if any */
char mount[PATH_MAX]; /**< Mount point or other contained directory. */
char smartctl[PATH_MAX]; /**< Options for smartctl. */
int smartignore[SMART_IGNORE_MAX]; /**< Attribues to ignore */
char file[PATH_MAX]; /**< File device. */
#ifdef _WIN32
char wfile[PATH_MAX]; /**< File device in Windows format. Like \\.\PhysicalDriveX, or \\?\Volume{X}. */
#endif
struct devinfo_struct* parent; /**< Pointer at the parent if any. */
struct devinfo_struct* split; /**< Pointer at first split if this one is not the first. */
struct smart_attr smart[SMART_COUNT]; /**< All smart values. */
uint64_t info[INFO_COUNT]; /**< Informational attributes not related to SMART telemetry. */
uint64_t access_stat; /**< Access stat info. */
char serial[SMART_MAX]; /**< Serial number. */
char family[SMART_MAX]; /**< Family. */
char model[SMART_MAX]; /**< Model. */
char interf[SMART_MAX]; /**< Interface of the device: ata, sata, pata, nvme, usb */
int power; /**< POWER mode. */
#if HAVE_THREAD
thread_id_t thread;
#endif
tommy_node node;
};
typedef struct devinfo_struct devinfo_t;
void device_name_set(devinfo_t* dev, const char* name, int index);
#define DEVICE_LIST 0
#define DEVICE_DOWN 1
#define DEVICE_UP 2
#define DEVICE_SMART 3
#define DEVICE_PROBE 4
#define DEVICE_DOWNIFUP 5
/**
* Query all the "high" level devices with the specified operation,
* and produces a list of "low" level devices to operate on.
*
* The passed "low" device list must be already initialized.
*/
int devquery(tommy_list* high, tommy_list* low, int operation);
/**
* Fill with fake data the device list.
*/
int devtest(tommy_list* high, tommy_list* low, int operation);
/**
* Query all the "low" devices and log their unique identifiers
*/
int devmap(void);
/**
* Get the ambient temperature in degree
*
* Return 0 if not available.
*/
int ambient_temperature(void);
/**
* Size of the spaceholder file for Windows to avoid the message of low disk space
*/
#define WINDOWS_SPACEHOLDER_SIZE (256 * 1024 * 1024)
/**
* Generic errors
*/
#define EINTERNAL EFAULT /**< Internal assertion failed. */
#define EDATA EIO /**< Silent data corruption. */
#define ESOFT EINVAL /**< Software error, like permission denied. */
#define EUSER EINVAL /**< Invalid value specified by the user. */
#define EEXTERNAL EINVAL /**< Invalid external interface behaviour. */
#define ECONTENT EINVAL /**< Invalid content file. */
#define EENVIRONMENT EINVAL /**< Invalid physical environment, like temperature too high. */
#endif