mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Refactor portable support
This commit is contained in:
parent
bb39c6ca3d
commit
db20376bca
17 changed files with 6036 additions and 5835 deletions
12
Makefile.am
12
Makefile.am
|
|
@ -65,7 +65,9 @@ snapraid_SOURCES = \
|
|||
cmdline/search.c \
|
||||
cmdline/thermal.c \
|
||||
cmdline/mingw.c \
|
||||
cmdline/unix.c
|
||||
cmdline/mingwapp.c \
|
||||
cmdline/unix.c \
|
||||
cmdline/unixapp.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
raid/raid.h \
|
||||
|
|
@ -114,18 +116,24 @@ noinst_HEADERS = \
|
|||
cmdline/search.h \
|
||||
cmdline/thermal.h \
|
||||
cmdline/mingw.h \
|
||||
cmdline/unix.h
|
||||
cmdline/unix.h \
|
||||
cmdline/os.h \
|
||||
cmdline/app.h
|
||||
|
||||
mktest_SOURCES = \
|
||||
cmdline/mktest.c \
|
||||
cmdline/mingw.c \
|
||||
cmdline/mingwapp.c \
|
||||
cmdline/unix.c \
|
||||
cmdline/unixapp.c \
|
||||
cmdline/support.c
|
||||
|
||||
mkstream_SOURCES = \
|
||||
cmdline/mkstream.c \
|
||||
cmdline/mingw.c \
|
||||
cmdline/mingwapp.c \
|
||||
cmdline/unix.c \
|
||||
cmdline/unixapp.c \
|
||||
cmdline/support.c \
|
||||
cmdline/util.c \
|
||||
cmdline/stream.c \
|
||||
|
|
|
|||
435
cmdline/app.h
Normal file
435
cmdline/app.h
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2026 Andrea Mazzoleni
|
||||
|
||||
#ifndef __APP_H
|
||||
#define __APP_H
|
||||
|
||||
/**
|
||||
* Include list support to have tommy_node.
|
||||
*/
|
||||
#include "tommyds/tommylist.h"
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Where snapshot are accessible
|
||||
*/
|
||||
#define SNAPSHOT_CONTAINER ".snapraid"
|
||||
|
||||
/**
|
||||
* Snapshots names
|
||||
*/
|
||||
#define SNAPSHOT_PENDING "pending"
|
||||
#define SNAPSHOT_STABLE "stable"
|
||||
|
||||
/*
|
||||
* 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
|
||||
* - ZFS: dataset mountpoint
|
||||
*/
|
||||
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/)
|
||||
*/
|
||||
char snapshot_dir[PATH_MAX];
|
||||
|
||||
/*
|
||||
* Dataset name for ZFS (e.g. "pool/data")
|
||||
* or VolumeName in Windows.
|
||||
* Empty string for filesystems where not applicable.
|
||||
*/
|
||||
char dataset[PATH_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize snapshot context from an arbitrary path.
|
||||
*
|
||||
* @param dir Full path inside the filesystem (must end with '/')
|
||||
* @param fss Output snapshot context
|
||||
* @return 0 on success, -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);
|
||||
|
||||
/*
|
||||
* 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 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. */
|
||||
|
||||
/****************************************************************************/
|
||||
/* 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);
|
||||
|
||||
/**
|
||||
* Global variable to identify if Ctrl+C is pressed.
|
||||
*/
|
||||
int app_global_interrupt(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -767,7 +767,7 @@ static void* io_reader_thread(void* arg)
|
|||
|
||||
if (task->state != TASK_STATE_READY) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Internal inconsistency: Unexpected state %d in reader for task at position %u in worker %u\n", task->state, task->position, worker->index);
|
||||
log_fatal(EINTERNAL, "Internal inconsistency: Unexpected state %d in reader for task at position %" PRIu64 " in worker %u\n", task->state, task->position, worker->index);
|
||||
os_abort();
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
@ -802,7 +802,7 @@ static void* io_writer_thread(void* arg)
|
|||
|
||||
if (task->state != TASK_STATE_READY) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EINTERNAL, "Internal inconsistency: Unexpected state %d in writer for task at position %u in worker %u\n", task->state, task->position, worker->index);
|
||||
log_fatal(EINTERNAL, "Internal inconsistency: Unexpected state %d in writer for task at position %" PRIu64 " in worker %u\n", task->state, task->position, worker->index);
|
||||
os_abort();
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
|
|||
1519
cmdline/mingw.c
1519
cmdline/mingw.c
File diff suppressed because it is too large
Load diff
|
|
@ -11,6 +11,41 @@
|
|||
/* map Windows name to POSIX name */
|
||||
#define strncasecmp _strnicmp
|
||||
|
||||
/****************************************************************************/
|
||||
/* conversion */
|
||||
|
||||
/**
|
||||
* Size in chars of conversion buffers for u8to16() and u16to8().
|
||||
*/
|
||||
#define CONV_MAX PATH_MAX
|
||||
|
||||
/**
|
||||
* Convert a path to the Windows format.
|
||||
*
|
||||
* If only_is_required is 1, the extended-length format is used only if required.
|
||||
*
|
||||
* The exact operation done is:
|
||||
* - If it's a '\\?\' or '\\.\' path, convert any '/' to '\'.
|
||||
* - If it's a disk designator path, like 'D:\' or 'D:/', it prepends '\\?\' to the path and convert any '/' to '\'.
|
||||
* - If it's a UNC path, like ''\\server'', it prepends '\\?\UNC\' to the path and convert any '/' to '\'.
|
||||
* - Otherwise, only the UTF conversion is done. In this case Windows imposes a limit of 260 chars, and automatically convert any '/' to '\'.
|
||||
*
|
||||
* For more details see:
|
||||
* Naming Files, Paths, and Namespaces
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#maxpath
|
||||
*/
|
||||
wchar_t* convert_arg(wchar_t* conv_buf, const char* src, int only_if_required);
|
||||
|
||||
#define convert(buf, a) convert_arg(buf, a, 0)
|
||||
#define convert_if_required(buf, a) convert_arg(buf, a, 1)
|
||||
|
||||
char* u16tou8(char* conv_buf, const wchar_t* src);
|
||||
|
||||
/**
|
||||
* Convert a generic string from UTF8 to UTF16.
|
||||
*/
|
||||
wchar_t* u8tou16(wchar_t* conv_buf, const char* src);
|
||||
|
||||
/****************************************************************************/
|
||||
/* file */
|
||||
|
||||
|
|
@ -390,6 +425,23 @@ int windows_get_file_attributes(const char* file);
|
|||
*/
|
||||
int windows_set_file_attributes(const char* file, int attributes);
|
||||
|
||||
/**
|
||||
* Convert Windows error to errno.
|
||||
*/
|
||||
void windows_errno(DWORD error);
|
||||
|
||||
/**
|
||||
* Return the executable directory.
|
||||
*
|
||||
* Ends with \
|
||||
*/
|
||||
const wchar_t* windows_exedir(void);
|
||||
|
||||
/**
|
||||
* Return if we are in the Wine environment.
|
||||
*/
|
||||
int windows_is_wine(void);
|
||||
|
||||
/****************************************************************************/
|
||||
/* thread */
|
||||
|
||||
|
|
|
|||
1051
cmdline/mingwapp.c
Normal file
1051
cmdline/mingwapp.c
Normal file
File diff suppressed because it is too large
Load diff
162
cmdline/os.h
Normal file
162
cmdline/os.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2026 Andrea Mazzoleni
|
||||
|
||||
#ifndef __OS_H
|
||||
#define __OS_H
|
||||
|
||||
/****************************************************************************/
|
||||
/* signal */
|
||||
|
||||
/**
|
||||
* Enable or disable signal handling.
|
||||
* @param enable 1 to enable signals, 0 to disable
|
||||
*/
|
||||
void os_signal_set(int enable);
|
||||
|
||||
/**
|
||||
* Initialize signal handling.
|
||||
*/
|
||||
void os_signal_init(void (*handler_term)(int sig), void (*handler_hup)(int sig));
|
||||
|
||||
/**
|
||||
* Restore signal handlers after fork in child process.
|
||||
* This resets signals to default handling.
|
||||
*/
|
||||
void os_signal_restore_after_fork(void);
|
||||
|
||||
/****************************************************************************/
|
||||
/* os */
|
||||
|
||||
/**
|
||||
* Initializes the system.
|
||||
*/
|
||||
void os_init(int opt);
|
||||
|
||||
/**
|
||||
* Deinitializes the system.
|
||||
*/
|
||||
void os_done(void);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Abort the process with a stacktrace.
|
||||
*/
|
||||
void os_abort(void) __noreturn;
|
||||
|
||||
/**
|
||||
* Clear the screen.
|
||||
*/
|
||||
void os_clear(void);
|
||||
|
||||
/**
|
||||
* Opaque handle tracking a process execution stream and its process ID.
|
||||
*
|
||||
* This structure encapsulates a standard buffered I/O stream tied to the
|
||||
* redirected standard output of a child process spawned via os_popen().
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
#define OS_FILE FILE
|
||||
#else
|
||||
typedef struct OS_FILE {
|
||||
FILE* fp; /**< Standard I/O buffered stream wrapper. */
|
||||
pid_t pid; /**< Process ID of the child process. */
|
||||
} OS_FILE;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Spawns a child process and opens a buffered stream to read its stdout.
|
||||
*
|
||||
* This function acts as a safer alternative to standard popen(), accepting an
|
||||
* explicit argument vector rather than evaluating a shell command string.
|
||||
*
|
||||
* The child process is completely isolated in its own process group (via setpgid)
|
||||
* with its stdin and stderr redirected to /dev/null. The underlying pipe is
|
||||
* created with O_CLOEXEC set to prevent leakage across concurrent forks.
|
||||
*
|
||||
* @param argv A NULL-terminated array of strings representing the argument vector.
|
||||
* argv[0] must contain the absolute path to the verified executable.
|
||||
* @param extra_args A mutable string containing additional arguments separated by
|
||||
* spaces, or NULL if no extra arguments are needed.
|
||||
* @return A pointer to an initialized OS_FILE structure on success, or NULL on failure.
|
||||
* On failure, errno is set appropriately.
|
||||
*/
|
||||
OS_FILE* os_popen(const char** argv, char* extra_args);
|
||||
|
||||
/**
|
||||
* Reads a line from an OS execution stream.
|
||||
*
|
||||
* Reads characters from the specified stream into the buffer string until
|
||||
* (size - 1) characters are read, a newline character is read and transferred,
|
||||
* or an end-of-file (EOF) condition is encountered. A terminating null
|
||||
* character is appended.
|
||||
*
|
||||
* @param s Pointer to an array of chars where the string read is stored.
|
||||
* @param size Maximum number of characters to be read (including the null character).
|
||||
* @param stream Pointer to the OS_FILE tracking context.
|
||||
*
|
||||
* @return On success, returns the buffer pointer @p s. If EOF is reached or a read
|
||||
* error occurs before any characters are read, returns NULL.
|
||||
*/
|
||||
char* os_fgets(char* s, int size, OS_FILE* stream);
|
||||
|
||||
/**
|
||||
* Closes an execution stream and reaps the associated child process.
|
||||
*
|
||||
* Closes the underlying buffered standard I/O stream, releases the allocated
|
||||
* tracking context container, and blocks until the associated child process
|
||||
* terminates to prevent the creation of zombie processes.
|
||||
*
|
||||
* @param stream Pointer to the active OS_FILE context to close and free.
|
||||
*
|
||||
* @return The termination status of the child process on success, or -1 on failure.
|
||||
* On failure, errno is set appropriately.
|
||||
*/
|
||||
int os_pclose(OS_FILE* stream);
|
||||
|
||||
/*
|
||||
* Fork and execute a verified executable, discarding all I/O.
|
||||
*
|
||||
* Spawns @argv[0] in a new process with stdin, stdout and stderr all
|
||||
* redirected to /dev/null. Use this for fire-and-forget tasks where the
|
||||
* child's output is not needed.
|
||||
*
|
||||
* The child is placed in its own process group (setpgid) to isolate it
|
||||
* from signals sent to the daemon's process group.
|
||||
*
|
||||
* @argv NULL-terminated argument vector. argv[0] must be the absolute path
|
||||
* to the executable.
|
||||
*
|
||||
* Returns the child execit status on success, or -1 on failure.
|
||||
*/
|
||||
int os_spawn_and_wait(const char** argv);
|
||||
|
||||
/**
|
||||
* Get the optimal CPU for the speed test
|
||||
*/
|
||||
int os_get_optimal_cpu(void);
|
||||
|
||||
/**
|
||||
* Fill memory with pseudo-random values
|
||||
*/
|
||||
int os_randomize(void* ptr, size_t size);
|
||||
|
||||
/**
|
||||
* Validates a string for exec.
|
||||
* Returns -1 if dangerous characters are detected, 0 otherwise.
|
||||
*/
|
||||
int os_validate_exec_input(const char* str);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -286,539 +286,5 @@ static inline int close_range_impl(unsigned int first, unsigned int last, unsign
|
|||
#include "unix.h"
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/* os */
|
||||
|
||||
/**
|
||||
* Default paths.
|
||||
*/
|
||||
void os_default_conf(char* dst, size_t dst_size, const char* argv0);
|
||||
|
||||
/**
|
||||
* Initialize signal handling.
|
||||
*/
|
||||
void os_signal_init();
|
||||
|
||||
/**
|
||||
* Restore signal handlers after fork in child process.
|
||||
* This resets signals to default handling.
|
||||
*/
|
||||
void os_signal_restore_after_fork(void);
|
||||
|
||||
/**
|
||||
* Global variable to identify if Ctrl+C is pressed.
|
||||
*/
|
||||
int os_global_interrupt(void);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Opaque handle tracking a process execution stream and its process ID.
|
||||
*
|
||||
* This structure encapsulates a standard buffered I/O stream tied to the
|
||||
* redirected standard output of a child process spawned via os_popen().
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
#define OS_FILE FILE
|
||||
#else
|
||||
typedef struct OS_FILE {
|
||||
FILE* fp; /**< Standard I/O buffered stream wrapper. */
|
||||
pid_t pid; /**< Process ID of the child process. */
|
||||
} OS_FILE;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Spawns a child process and opens a buffered stream to read its stdout.
|
||||
*
|
||||
* This function acts as a safer alternative to standard popen(), accepting an
|
||||
* explicit argument vector rather than evaluating a shell command string.
|
||||
*
|
||||
* The child process is completely isolated in its own process group (via setpgid)
|
||||
* with its stdin and stderr redirected to /dev/null. The underlying pipe is
|
||||
* created with O_CLOEXEC set to prevent leakage across concurrent forks.
|
||||
*
|
||||
* @param argv A NULL-terminated array of strings representing the argument vector.
|
||||
* argv[0] must contain the absolute path to the verified executable.
|
||||
* @param extra_args A mutable string containing additional arguments separated by
|
||||
* spaces, or NULL if no extra arguments are needed.
|
||||
* @return A pointer to an initialized OS_FILE structure on success, or NULL on failure.
|
||||
* On failure, errno is set appropriately.
|
||||
*/
|
||||
OS_FILE* os_popen(const char** argv, char* extra_args);
|
||||
|
||||
/**
|
||||
* Reads a line from an OS execution stream.
|
||||
*
|
||||
* Reads characters from the specified stream into the buffer string until
|
||||
* (size - 1) characters are read, a newline character is read and transferred,
|
||||
* or an end-of-file (EOF) condition is encountered. A terminating null
|
||||
* character is appended.
|
||||
*
|
||||
* @param s Pointer to an array of chars where the string read is stored.
|
||||
* @param size Maximum number of characters to be read (including the null character).
|
||||
* @param stream Pointer to the OS_FILE tracking context.
|
||||
*
|
||||
* @return On success, returns the buffer pointer @p s. If EOF is reached or a read
|
||||
* error occurs before any characters are read, returns NULL.
|
||||
*/
|
||||
char* os_fgets(char* s, int size, OS_FILE* stream);
|
||||
|
||||
/**
|
||||
* Closes an execution stream and reaps the associated child process.
|
||||
*
|
||||
* Closes the underlying buffered standard I/O stream, releases the allocated
|
||||
* tracking context container, and blocks until the associated child process
|
||||
* terminates to prevent the creation of zombie processes.
|
||||
*
|
||||
* @param stream Pointer to the active OS_FILE context to close and free.
|
||||
*
|
||||
* @return The termination status of the child process on success, or -1 on failure.
|
||||
* On failure, errno is set appropriately.
|
||||
*/
|
||||
int os_pclose(OS_FILE* stream);
|
||||
|
||||
/**
|
||||
* Get the optimal CPU for the speed test
|
||||
*/
|
||||
int os_get_optimal_cpu(void);
|
||||
|
||||
/**
|
||||
* Fill memory with pseudo-random values
|
||||
*/
|
||||
int os_randomize(void* ptr, size_t size);
|
||||
|
||||
/****************************************************************************/
|
||||
/* app */
|
||||
|
||||
/**
|
||||
* Include list support to have tommy_node.
|
||||
*/
|
||||
#include "tommyds/tommylist.h"
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Where snapshot are accessible
|
||||
*/
|
||||
#define SNAPSHOT_CONTAINER ".snapraid"
|
||||
|
||||
/**
|
||||
* Snapshots names
|
||||
*/
|
||||
#define SNAPSHOT_PENDING "pending"
|
||||
#define SNAPSHOT_STABLE "stable"
|
||||
|
||||
/*
|
||||
* 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
|
||||
* - ZFS: dataset mountpoint
|
||||
*/
|
||||
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/)
|
||||
*/
|
||||
char snapshot_dir[PATH_MAX];
|
||||
|
||||
/*
|
||||
* Dataset name for ZFS (e.g. "pool/data")
|
||||
* or VolumeName in Windows.
|
||||
* Empty string for filesystems where not applicable.
|
||||
*/
|
||||
char dataset[PATH_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize snapshot context from an arbitrary path.
|
||||
*
|
||||
* @param dir Full path inside the filesystem (must end with '/')
|
||||
* @param fss Output snapshot context
|
||||
* @return 0 on success, -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);
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "portable.h"
|
||||
|
||||
#include "os.h"
|
||||
#include "snapraid.h"
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
@ -951,7 +952,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
lock_init();
|
||||
|
||||
/* defaults */
|
||||
os_default_conf(conf, sizeof(conf), argv[0]);
|
||||
app_default_conf(conf, sizeof(conf), argv[0]);
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
opt.io_error_limit = 100;
|
||||
blockstart = 0;
|
||||
|
|
@ -1413,18 +1414,21 @@ int snapraid_main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
os_init(opt.force_scan_winfind);
|
||||
app_init();
|
||||
raid_init();
|
||||
crc32c_init();
|
||||
random_reseed();
|
||||
|
||||
if (speedtest != 0) {
|
||||
speed(speed_test_period, speed_test_disks_number, speed_test_blocks_size);
|
||||
app_done();
|
||||
os_done();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (gen_conf != 0) {
|
||||
generate_configuration(gen_conf);
|
||||
app_done();
|
||||
os_done();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
|
@ -1824,7 +1828,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
memory();
|
||||
|
||||
/* intercept signals while operating */
|
||||
os_signal_init();
|
||||
os_signal_init(app_signal_handler, app_signal_handler);
|
||||
|
||||
#if HAVE_CHECKER
|
||||
/* run a test command if required */
|
||||
|
|
@ -1877,7 +1881,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
memory();
|
||||
|
||||
/* intercept signals while operating */
|
||||
os_signal_init();
|
||||
os_signal_init(app_signal_handler, app_signal_handler);
|
||||
|
||||
ret = state_dry(&state, blockstart, blockcount);
|
||||
} else if (operation == OPERATION_REHASH) {
|
||||
|
|
@ -1893,7 +1897,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
* The signal protection is meant only for saving the content file,
|
||||
* or an early stop of long operations.
|
||||
*/
|
||||
os_signal_init();
|
||||
os_signal_init(app_signal_handler, app_signal_handler);
|
||||
|
||||
/* save the new state if required */
|
||||
if (state.need_write)
|
||||
|
|
@ -1907,7 +1911,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
memory();
|
||||
|
||||
/* intercept signals while operating */
|
||||
os_signal_init();
|
||||
os_signal_init(app_signal_handler, app_signal_handler);
|
||||
|
||||
state_snapshot_read(&state);
|
||||
|
||||
|
|
@ -1920,7 +1924,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
state_read(&state);
|
||||
|
||||
/* intercept signals while operating */
|
||||
os_signal_init();
|
||||
os_signal_init(app_signal_handler, app_signal_handler);
|
||||
|
||||
state_write(&state);
|
||||
|
||||
|
|
@ -1946,7 +1950,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
* The signal protection is meant only for saving the content file,
|
||||
* or an early stop of long operations.
|
||||
*/
|
||||
os_signal_init();
|
||||
os_signal_init(app_signal_handler, app_signal_handler);
|
||||
|
||||
if (state.need_write)
|
||||
state_write(&state);
|
||||
|
|
@ -2035,7 +2039,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
memory();
|
||||
|
||||
/* intercept signals while operating */
|
||||
os_signal_init();
|
||||
os_signal_init(app_signal_handler, app_signal_handler);
|
||||
|
||||
state_snapshot_write(&state, &filterlist_disk);
|
||||
|
||||
|
|
@ -2077,6 +2081,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
tommy_list_foreach(&filterlist_file, (tommy_foreach_func*)filter_free);
|
||||
tommy_list_foreach(&filterlist_disk, (tommy_foreach_func*)filter_free);
|
||||
|
||||
app_done();
|
||||
os_done();
|
||||
lock_done();
|
||||
|
||||
|
|
@ -2086,19 +2091,19 @@ int snapraid_main(int argc, char* argv[])
|
|||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (os_global_interrupt()) {
|
||||
if (app_global_interrupt()) {
|
||||
/* LCOV_EXCL_START */
|
||||
#ifdef _WIN32
|
||||
exit(STATUS_CONTROL_C_EXIT);
|
||||
#else
|
||||
/* restore default handler */
|
||||
signal(os_global_interrupt(), SIG_DFL);
|
||||
signal(app_global_interrupt(), SIG_DFL);
|
||||
|
||||
/* raise the signal again*/
|
||||
raise(os_global_interrupt());
|
||||
raise(app_global_interrupt());
|
||||
|
||||
/* if raise() didn't terminate */
|
||||
_exit(128 + os_global_interrupt());
|
||||
_exit(128 + app_global_interrupt());
|
||||
#endif
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4973,7 +4973,7 @@ int state_progress_begin(struct snapraid_state* state, block_off_t blockstart, b
|
|||
return -1;
|
||||
|
||||
/* stop if requested */
|
||||
if (os_global_interrupt()) {
|
||||
if (app_global_interrupt()) {
|
||||
/* LCOV_EXCL_START */
|
||||
if (!state->opt.gui) {
|
||||
msg_status("Not starting due to interruption\n");
|
||||
|
|
@ -5494,7 +5494,7 @@ int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_o
|
|||
}
|
||||
|
||||
/* stop if requested */
|
||||
if (os_global_interrupt()) {
|
||||
if (app_global_interrupt()) {
|
||||
/* LCOV_EXCL_START */
|
||||
if (!state->opt.gui) {
|
||||
msg_bar("\n");
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
#ifndef __SUPPORT_H
|
||||
#define __SUPPORT_H
|
||||
|
||||
#include "os.h"
|
||||
#include "app.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* lock */
|
||||
|
||||
|
|
|
|||
|
|
@ -328,7 +328,7 @@ void state_thermal_cooldown(struct snapraid_state* state)
|
|||
sleep_time -= 30;
|
||||
}
|
||||
|
||||
if (!os_global_interrupt()) { /* don't wake-up if we are interrupting */
|
||||
if (!app_global_interrupt()) { /* don't wake-up if we are interrupting */
|
||||
log_tag("thermal:spinup\n");
|
||||
|
||||
/* spinup */
|
||||
|
|
|
|||
4103
cmdline/unix.c
4103
cmdline/unix.c
File diff suppressed because it is too large
Load diff
|
|
@ -12,11 +12,6 @@
|
|||
#define O_BINARY 0 /**< Not used in Unix. */
|
||||
#define O_SEQUENTIAL 0 /**< In Unix posix_fadvise() shall be used. */
|
||||
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
int posix_fadvise_wrapper(int fd, off_t offset, off_t len, int advice);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* If nanoseconds are not supported, we report the special STAT_NSEC_INVALID value,
|
||||
* to mark that it's undefined.
|
||||
|
|
@ -34,6 +29,63 @@ int posix_fadvise_wrapper(int fd, off_t offset, off_t len, int advice);
|
|||
#define STAT_NSEC(st) STAT_NSEC_INVALID
|
||||
#endif
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
/**
|
||||
* Wrapper around posix_fadvise() that handles ENOENT by converting it to
|
||||
* ENOSYS.
|
||||
*
|
||||
* Why can posix_fadvise() return ENOENT?
|
||||
*
|
||||
* According to POSIX and the Linux kernel's standard fadvise64 implementation
|
||||
* (mm/fadvise.c), posix_fadvise() should only return EBADF (bad file
|
||||
* descriptor), ESPIPE (fd refers to a pipe or FIFO), or EINVAL (invalid
|
||||
* advice value or arguments). ENOENT is never generated by the kernel's
|
||||
* generic path.
|
||||
*
|
||||
* However, the VFS layer allows individual filesystems to provide their own
|
||||
* .fadvise() method, which is called instead of (or in addition to) the
|
||||
* generic implementation. Certain filesystem drivers can return ENOENT in
|
||||
* this method for reasons that are completely unrelated to the file not
|
||||
* existing (the file is clearly open, we have a valid fd):
|
||||
*
|
||||
* - NFS: A stale NFS inode can be revalidated against the server while
|
||||
* fadvise is being processed. If the server-side file has been removed or
|
||||
* renamed, the revalidation discovers the inode is gone and returns
|
||||
* -ENOENT, even though the client still holds an open file descriptor.
|
||||
*
|
||||
* - FUSE-based filesystems (mergerfs, sshfs, rclone, etc.): The FUSE daemon
|
||||
* receives the fadvise request and may forward it to an underlying backend.
|
||||
* If that backend cannot find the file (e.g., the backing path has changed,
|
||||
* the FUSE daemon is partially initialised, or the daemon translates the
|
||||
* request and the target layer returns an error), the daemon may return
|
||||
* ENOENT to the kernel.
|
||||
*
|
||||
* - Overlayfs (used by Docker, Podman, LXC): The overlayfs .fadvise()
|
||||
* implementation delegates to the lower or upper layer. If the lookup
|
||||
* through the overlay stack fails to find the real inode (e.g., the upper
|
||||
* directory entry is absent and the lower copy is in a weird state), the
|
||||
* delegation can return ENOENT.
|
||||
*
|
||||
* - Other network or virtual filesystems: Any out-of-tree or in-tree
|
||||
* filesystem that implements .fadvise() and performs a path or inode
|
||||
* lookup as part of processing the hint can propagate ENOENT if that
|
||||
* internal lookup fails.
|
||||
*
|
||||
* Because posix_fadvise() is always an advisory hint, it never affects
|
||||
* correctness, only performance, it is safe to treat unexpected errors,
|
||||
* including ENOENT, as "not supported" (ENOSYS) and continue.
|
||||
*/
|
||||
int posix_fadvise_wrapper(int fd, off_t offset, off_t len, int advice);
|
||||
#endif
|
||||
|
||||
#if !HAVE_EACCESS
|
||||
/**
|
||||
* Check effective user's permissions for a file.
|
||||
* Conceptually identical to access(), but uses the effective UID/GID.
|
||||
*/
|
||||
int eaccess(const char* pathname, int mode);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Open a file with the O_NOATIME flag to avoid to update the access time.
|
||||
*/
|
||||
|
|
|
|||
3896
cmdline/unixapp.c
Normal file
3896
cmdline/unixapp.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -4,6 +4,7 @@
|
|||
#ifndef __UTIL_H
|
||||
#define __UTIL_H
|
||||
|
||||
#include "tommyds/tommytypes.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* memory */
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ AC_CANONICAL_HOST
|
|||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_TARGET
|
||||
AM_INIT_AUTOMAKE([foreign no-dependencies subdir-objects])
|
||||
AC_CONFIG_SRCDIR([cmdline/snapraid.c])
|
||||
AC_CONFIG_SRCDIR([cmdline/portable.h])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl Enables as many system-specific extensions as reasonably possible
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue