mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Some checks are pending
Build / build-mac (push) Waiting to run
Build / build-linux (push) Waiting to run
Build / build-arm32 (push) Waiting to run
Build / build-win (push) Waiting to run
Build / build-freebsd (push) Waiting to run
Build / build-musl (push) Waiting to run
CodeQL Advanced / Analyze (c-cpp) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
514 lines
15 KiB
C
514 lines
15 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2026 Andrea Mazzoleni
|
|
|
|
#ifndef __APP_H
|
|
#define __APP_H
|
|
|
|
#include "os/os.h"
|
|
|
|
/**
|
|
* Include list support to have tommy_node.
|
|
*/
|
|
#include "tommyds/tommylist.h"
|
|
|
|
#define STATIC_ASSERT(name, cond) typedef char static_assert_ ## name[(cond) ? 1 : -1]
|
|
|
|
#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. */
|
|
#define ESYSLOG EINVAL /**< Syslog error. */
|
|
|
|
/**
|
|
* Basic block position type.
|
|
*/
|
|
typedef uint64_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);
|
|
|
|
/**
|
|
* Filesystem identity for directory boundary checks.
|
|
*/
|
|
struct fsidentity {
|
|
uint64_t device; /**< Device identifier (st_dev). */
|
|
uint64_t mnt_id; /**< Mount identifier (stx_mnt_id or 0). */
|
|
uint64_t subvol; /**< Subvolume identifier (stx_subvol or 0). */
|
|
};
|
|
|
|
/**
|
|
* Get the filesystem identity of a path.
|
|
*
|
|
* The caller already provides the normal stat information.
|
|
* This function must NOT repeat lstat()/stat() for normal metadata.
|
|
*
|
|
* mnt_id and subvol are set to zero when unavailable.
|
|
*
|
|
* Return 0 on success, -1 on error.
|
|
*/
|
|
int fsidentity(const char* path, const struct stat* st, struct fsidentity* identity);
|
|
|
|
/**
|
|
* 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);
|
|
|
|
/**
|
|
* Where snapshot are accessible
|
|
*/
|
|
#define SNAPSHOT_CONTAINER ".snapraid"
|
|
|
|
/**
|
|
* Snapshots names
|
|
*/
|
|
#define SNAPSHOT_PENDING "pending"
|
|
#define SNAPSHOT_STABLE "stable"
|
|
#define SNAPSHOT_SCAN "scan"
|
|
#define SNAPSHOT_RETIRING_SUFFIX ".retiring"
|
|
|
|
/*
|
|
* Snapshots context
|
|
*/
|
|
struct fssnapshot_struct {
|
|
/**
|
|
* Filesystem magic number
|
|
* 0 if no snapshot support
|
|
*/
|
|
uint32_t magic;
|
|
|
|
/**
|
|
* Root directory of the volume/dataset mountpoint.
|
|
* It ALWAYS terminates with /
|
|
* - Btrfs/Bcachefs: subvolume root (canonical path via realpath)
|
|
* - ZFS: dataset mountpoint (canonical path via realpath)
|
|
* - Windows: volume mountpoint (via GetVolumePathName)
|
|
*/
|
|
char root_dir[PATH_MAX];
|
|
|
|
/**
|
|
* Directory where snapshots are accessible as subdirectories.
|
|
* It ALWAYS terminates with /
|
|
* - Btrfs/Bcachefs: real directory (e.g. <root>/.snapraid/)
|
|
* - ZFS: virtual directory (e.g. <root>/.zfs/snapshot/)
|
|
* - Windows: symlink container directory (e.g. <root>/.snapraid/)
|
|
*/
|
|
char snapshot_dir[PATH_MAX];
|
|
|
|
/**
|
|
* Data directory relative to root_dir in snapshots.
|
|
*
|
|
* It NEVER starts with '/'.
|
|
* It is empty if the configured data directory is exactly root_dir.
|
|
* If non-empty, it ALWAYS terminates with '/'.
|
|
* - Unix: relative suffix computed from the realpath() canonical representation
|
|
* - Windows: relative suffix computed from the GetFullPathNameW() absolute representation without resolving reparse points
|
|
*/
|
|
char sub_dir[PATH_MAX];
|
|
|
|
/*
|
|
* Dataset name for ZFS (e.g. "pool/data")
|
|
* or VolumeName in Windows (e.g. "\\?\Volume{guid}\").
|
|
* Empty string for filesystems where not applicable.
|
|
*/
|
|
char dataset[PATH_MAX];
|
|
};
|
|
|
|
/**
|
|
* Initialize snapshot context from an arbitrary path.
|
|
*
|
|
* @param dir Path inside the filesystem (must end with '/')
|
|
* @param fss Output snapshot context
|
|
* @return 0 on success, 1 if snapshots are unsupported, -1 on failure
|
|
*/
|
|
int fssnapshot_mount(const char* dir, struct fssnapshot_struct* fss);
|
|
|
|
/**
|
|
* Unmount the snapshots.
|
|
*
|
|
* Required in Windows to remove the temporary symlinks.
|
|
*
|
|
* @param fss Snapshot context
|
|
*/
|
|
void fssnapshot_unmount(const struct fssnapshot_struct* fss);
|
|
|
|
/**
|
|
* Stat the snapshot directory.
|
|
*
|
|
* @param fss Snapshot context
|
|
* @param name Snapshot name
|
|
* @param st Set the stat info
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int fssnapshot_stat(struct fssnapshot_struct* fss, const char* name, struct stat* st);
|
|
|
|
/**
|
|
* Create a snapshot of the filesystem root/dataset.
|
|
*
|
|
* @param fss Snapshot context
|
|
* @param name Snapshot name
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int fssnapshot_create(const struct fssnapshot_struct* fss, const char* name);
|
|
|
|
/**
|
|
* Delete a snapshot.
|
|
*
|
|
* @param fss Snapshot context
|
|
* @param name Snapshot name
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int fssnapshot_delete(const struct fssnapshot_struct* fss, const char* name);
|
|
|
|
/**
|
|
* Rename a snapshot.
|
|
*
|
|
* @param fss Snapshot context
|
|
* @param old_name Existing snapshot name
|
|
* @param new_name New snapshot name
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int fssnapshot_rename(const struct fssnapshot_struct* fss, const char* old_name, const char* new_name);
|
|
|
|
/**
|
|
* Replace a snapshot while preserving the previous destination until the new one is published.
|
|
*
|
|
* An interrupted replacement is recovered automatically by fssnapshot_mount().
|
|
*
|
|
* @param fss Snapshot context
|
|
* @param old_name Existing source snapshot name
|
|
* @param new_name Destination snapshot name
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int fssnapshot_replace(struct fssnapshot_struct* fss, const char* old_name, const char* new_name);
|
|
|
|
/**
|
|
* Get the path where a logical snapshot is accessible.
|
|
*
|
|
* @param fss Snapshot context
|
|
* @param name Logical snapshot name
|
|
* @param path Output path
|
|
* @param path_size Size of path
|
|
* @return 0 on success, -1 on failure
|
|
*/
|
|
int fssnapshot_path(const struct fssnapshot_struct* fss, const char* name, char* path, size_t path_size);
|
|
|
|
/*
|
|
* 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
|
|
|
|
/**
|
|
* 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 name or value length.
|
|
*/
|
|
#define SMART_MAX 128
|
|
|
|
/**
|
|
* Smart ignore structure.
|
|
*/
|
|
struct smartignore_struct {
|
|
int attr_index; /**< 0 if not set */
|
|
char attr_name[SMART_MAX]; /**< Empty if not set */
|
|
};
|
|
|
|
/**
|
|
* 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[SMART_MAX]; /**< 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
|
|
|
|
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[SMART_MAX]; /**< Custom option for smartctl. Empty means auto. */
|
|
char smartctl_info[SMART_MAX]; /* Info options for smartctl. Empty means -a. */
|
|
struct smartignore_struct smartignore[SMART_IGNORE_MAX]; /**< Attributes 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. */
|
|
int is_array; /**< 1 if this is an array disk (data or parity), 0 if extra disk. */
|
|
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
|
|
|
|
/**
|
|
* Sync filesystems associated with the "high" level devices.
|
|
*/
|
|
void devsync(tommy_list* high);
|
|
|
|
/**
|
|
* Query all the "high" level devices and produce a list of "low" level devices.
|
|
*
|
|
* The passed "low" device list must be already initialized.
|
|
*/
|
|
int devquery(tommy_list* high, tommy_list* low);
|
|
|
|
/**
|
|
* Execute an operation on the "low" level devices.
|
|
*/
|
|
int devrun(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)
|
|
|
|
/****************************************************************************/
|
|
/* app */
|
|
|
|
/**
|
|
* Initializes the application.
|
|
*/
|
|
void app_init(void);
|
|
|
|
/**
|
|
* Deinitializes the application.
|
|
*/
|
|
void app_done(void);
|
|
|
|
/**
|
|
* Default paths.
|
|
*/
|
|
void app_default_conf(char* dst, size_t dst_size, const char* argv0);
|
|
|
|
/**
|
|
* Signal handler.
|
|
*/
|
|
void app_signal_handler(int signum);
|
|
|
|
/****************************************************************************/
|
|
/* machine */
|
|
|
|
#define MACHINEINFO_MAX 256
|
|
|
|
/**
|
|
* Host machine and CPU information.
|
|
*/
|
|
struct machineinfo_struct {
|
|
char cpu_brand[MACHINEINFO_MAX]; /**< CPU model brand name. */
|
|
char cpu_vendor[MACHINEINFO_MAX]; /**< CPU vendor string (e.g. GenuineIntel, AuthenticAMD). */
|
|
char cpu_arch[MACHINEINFO_MAX]; /**< CPU architecture string. */
|
|
char cpu_clock[MACHINEINFO_MAX]; /**< CPU clock frequency description. */
|
|
unsigned cpu_family; /**< CPU family id. */
|
|
unsigned cpu_model; /**< CPU model id. */
|
|
unsigned cpu_stepping; /**< CPU stepping id. */
|
|
unsigned cpu_cores_physical; /**< Number of physical CPU cores (0 if unavailable). */
|
|
unsigned cpu_cores_logical; /**< Number of logical CPU threads (0 if unavailable). */
|
|
uint64_t cache_l1_data; /**< L1 data cache in bytes (0 if unavailable). */
|
|
uint64_t cache_l1_instruction; /**< L1 instruction cache in bytes (0 if unavailable). */
|
|
uint64_t cache_l2; /**< L2 cache in bytes (0 if unavailable). */
|
|
uint64_t cache_l3; /**< L3 cache in bytes (0 if unavailable). */
|
|
uint64_t memory_total_bytes; /**< Total system RAM in bytes (0 if unavailable). */
|
|
uint32_t memory_page_size; /**< Page size in bytes (0 if unavailable). */
|
|
char os_name[MACHINEINFO_MAX]; /**< Operating system name and kernel version. */
|
|
char os_distribution[MACHINEINFO_MAX]; /**< Operating system distribution name. */
|
|
char system_model[MACHINEINFO_MAX]; /**< Motherboard or system model name. */
|
|
char hypervisor[MACHINEINFO_MAX]; /**< Hypervisor name (empty if bare metal). */
|
|
};
|
|
|
|
/**
|
|
* Query host machine information.
|
|
*/
|
|
void machineinfo(struct machineinfo_struct* info);
|
|
|
|
#endif
|
|
|