mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-25 18:40:19 -04:00
Merge the CLI and Daemon portable interface
This commit is contained in:
parent
3302518566
commit
3ce5a5b4e5
46 changed files with 4110 additions and 2042 deletions
27
Makefile.am
27
Makefile.am
|
|
@ -11,7 +11,9 @@ endif
|
|||
|
||||
# Windows
|
||||
if HOST_WINDOWS
|
||||
snapraid_LDADD = resource_res.o
|
||||
snapraid_LDADD = resource_res.o -ladvapi32 -luserenv
|
||||
mktest_LDADD = -ladvapi32 -luserenv
|
||||
mkstream_LDADD = -ladvapi32 -luserenv
|
||||
|
||||
resource_res.o: cmdline/resource.rc cmdline/resource.manifest
|
||||
$(WINDRES) --include-dir=. --include-dir=$(srcdir) $< -o $@
|
||||
|
|
@ -64,9 +66,9 @@ snapraid_SOURCES = \
|
|||
cmdline/import.c \
|
||||
cmdline/search.c \
|
||||
cmdline/thermal.c \
|
||||
cmdline/mingw.c \
|
||||
os/mingw.c \
|
||||
cmdline/mingwapp.c \
|
||||
cmdline/unix.c \
|
||||
os/unix.c \
|
||||
cmdline/unixapp.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
|
|
@ -92,7 +94,6 @@ noinst_HEADERS = \
|
|||
tommyds/tommyhashdyn.c \
|
||||
tommyds/tommylist.c \
|
||||
tommyds/tommytree.c \
|
||||
cmdline/portable.h \
|
||||
cmdline/snapraid.h \
|
||||
cmdline/io.h \
|
||||
cmdline/bw.h \
|
||||
|
|
@ -115,24 +116,26 @@ noinst_HEADERS = \
|
|||
cmdline/import.h \
|
||||
cmdline/search.h \
|
||||
cmdline/thermal.h \
|
||||
cmdline/mingw.h \
|
||||
cmdline/unix.h \
|
||||
cmdline/os.h \
|
||||
cmdline/app.h
|
||||
cmdline/app.h \
|
||||
os/os.h \
|
||||
os/mingw.h \
|
||||
os/unix.h \
|
||||
os/os.h \
|
||||
os/portable.h
|
||||
|
||||
mktest_SOURCES = \
|
||||
cmdline/mktest.c \
|
||||
cmdline/mingw.c \
|
||||
os/mingw.c \
|
||||
cmdline/mingwapp.c \
|
||||
cmdline/unix.c \
|
||||
os/unix.c \
|
||||
cmdline/unixapp.c \
|
||||
cmdline/support.c
|
||||
|
||||
mkstream_SOURCES = \
|
||||
cmdline/mkstream.c \
|
||||
cmdline/mingw.c \
|
||||
os/mingw.c \
|
||||
cmdline/mingwapp.c \
|
||||
cmdline/unix.c \
|
||||
os/unix.c \
|
||||
cmdline/unixapp.c \
|
||||
cmdline/support.c \
|
||||
cmdline/util.c \
|
||||
|
|
|
|||
|
|
@ -4,11 +4,22 @@
|
|||
#ifndef __APP_H
|
||||
#define __APP_H
|
||||
|
||||
#include "os/os.h"
|
||||
|
||||
/**
|
||||
* Include list support to have tommy_node.
|
||||
*/
|
||||
#include "tommyds/tommylist.h"
|
||||
|
||||
#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.
|
||||
*/
|
||||
|
|
@ -34,34 +45,6 @@ static inline int hardlink(const char* a, const char* b)
|
|||
*/
|
||||
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.
|
||||
|
|
@ -393,17 +376,6 @@ int ambient_temperature(void);
|
|||
*/
|
||||
#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 */
|
||||
|
||||
|
|
@ -427,10 +399,5 @@ void app_default_conf(char* dst, size_t dst_size, const char* argv0);
|
|||
*/
|
||||
void app_signal_handler(int signum);
|
||||
|
||||
/**
|
||||
* Global variable to identify if Ctrl+C is pressed.
|
||||
*/
|
||||
int app_global_interrupt(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2016 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "bw.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "util.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2015 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "state.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "util.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "support.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "support.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "import.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2016 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "io.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2026 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "parity.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2025 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "snapraid.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#ifdef __MINGW32__ /* Only for MingW */
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ int exit_sync_needed = 2;
|
|||
|
||||
volatile int global_interrupt = 0;
|
||||
|
||||
int app_global_interrupt(void)
|
||||
int os_signal_interrupt(void)
|
||||
{
|
||||
return global_interrupt;
|
||||
}
|
||||
|
|
@ -33,6 +33,598 @@ void app_signal_handler(int signum)
|
|||
global_interrupt = signum;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* fs */
|
||||
|
||||
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)
|
||||
{
|
||||
wchar_t conv_buf[CONV_MAX];
|
||||
|
||||
/* all FAT/exFAT/NTFS when managed from Windows have persistent inodes */
|
||||
if (has_persistent_inode)
|
||||
*has_persistent_inode = 1;
|
||||
|
||||
/* NTFS doesn't synchronize hardlinks metadata */
|
||||
if (has_syncronized_hardlinks)
|
||||
*has_syncronized_hardlinks = 0;
|
||||
|
||||
if (free_space || total_space) {
|
||||
ULARGE_INTEGER total_bytes;
|
||||
ULARGE_INTEGER total_free_bytes;
|
||||
DWORD attr;
|
||||
char dir[PATH_MAX];
|
||||
|
||||
if (strlen(path) + 1 > sizeof(dir)) {
|
||||
windows_errno(ERROR_BUFFER_OVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(dir, path);
|
||||
|
||||
/* get the file attributes */
|
||||
attr = GetFileAttributesW(convert(conv_buf, dir));
|
||||
if (attr == INVALID_FILE_ATTRIBUTES) {
|
||||
DWORD error = GetLastError();
|
||||
|
||||
if (error != ERROR_FILE_NOT_FOUND) {
|
||||
windows_errno(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it doesn't exist, we assume a file
|
||||
* and we check for the containing dir
|
||||
*/
|
||||
attr = 0;
|
||||
}
|
||||
|
||||
/* if it's not a directory, truncate the file name */
|
||||
if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
char* slash = strrchr(dir, '/');
|
||||
|
||||
/**
|
||||
* Cut the file name, but leave the last slash.
|
||||
*
|
||||
* This is done because a MSDN comment about using of UNC paths.
|
||||
*
|
||||
* MSDN 'GetDiskFreeSpaceEx function'
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa364937%28v=vs.85%29.aspx
|
||||
* If this parameter is a UNC name, it must include a trailing backslash,
|
||||
* for example, "\\MyServer\MyShare\".
|
||||
*/
|
||||
if (slash)
|
||||
slash[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the free space of the directory
|
||||
* note that it must be a directory
|
||||
*/
|
||||
if (!GetDiskFreeSpaceExW(convert(conv_buf, dir), 0, &total_bytes, &total_free_bytes)) {
|
||||
windows_errno(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (total_space)
|
||||
*total_space = total_bytes.QuadPart;
|
||||
if (free_space)
|
||||
*free_space = total_free_bytes.QuadPart;
|
||||
}
|
||||
|
||||
if (fstype && fslabel) {
|
||||
wchar_t volume_root[PATH_MAX];
|
||||
wchar_t fs_name[PATH_MAX];
|
||||
wchar_t vol_name[PATH_MAX];
|
||||
|
||||
fstype[0] = 0;
|
||||
fslabel[0] = 0;
|
||||
if (GetVolumePathNameW(convert(conv_buf, path), volume_root, PATH_MAX)) {
|
||||
if (GetVolumeInformationW(volume_root, vol_name, PATH_MAX, 0, 0, 0, fs_name, PATH_MAX)) {
|
||||
char u8[CONV_MAX];
|
||||
size_t len;
|
||||
char* ret;
|
||||
|
||||
ret = u16tou8_mayfail(u8, sizeof(u8), fs_name, wcslen(fs_name), &len);
|
||||
if (ret != 0 && len + 1 <= fstype_size) {
|
||||
memcpy(fstype, u8, len);
|
||||
fstype[len] = 0;
|
||||
}
|
||||
|
||||
ret = u16tou8_mayfail(u8, sizeof(u8), vol_name, wcslen(vol_name), &len);
|
||||
if (ret != 0 && len + 1 <= fslabel_size) {
|
||||
memcpy(fslabel, u8, len);
|
||||
fslabel[len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* snapshot */
|
||||
|
||||
size_t windows_direct_size(void)
|
||||
{
|
||||
SYSTEM_INFO si;
|
||||
|
||||
GetSystemInfo(&si);
|
||||
|
||||
/*
|
||||
* MSDN 'File Buffering'
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/cc644950%28v=vs.85%29.aspx
|
||||
*
|
||||
* "Therefore, in most situations, page-aligned memory will also be sector-aligned,"
|
||||
* "because the case where the sector size is larger than the page size is rare."
|
||||
*/
|
||||
return si.dwPageSize;
|
||||
}
|
||||
|
||||
#define WINDOWS_NTFS_MAGIC 0x5346544E /* 'N','T','F','S' */
|
||||
|
||||
#define PS_CMD_MAX 4096
|
||||
|
||||
#define SNAPSHOT_GUID ".guid"
|
||||
|
||||
/*
|
||||
* PowerShell helper
|
||||
*
|
||||
* Runs a PowerShell one-liner via _popen() and captures the first line
|
||||
* of stdout into `out` as UTF-8.
|
||||
* `out` may be NULL when output is not needed.
|
||||
*
|
||||
* The caller builds `command` as a string using snprintf().
|
||||
* Any embedded path literals must use single quotes so they survive
|
||||
* the outer double-quote wrapping passed to cmd.exe.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
static int windows_ps(const char* ps_command, char* out, size_t out_size)
|
||||
{
|
||||
char cmd[PS_CMD_MAX];
|
||||
FILE* fp;
|
||||
int ret;
|
||||
|
||||
ret = snprintf(cmd, sizeof(cmd), "powershell.exe -NoProfile -NonInteractive -Command \"%s\" 2>nul", ps_command);
|
||||
if (ret < 0 || ret >= (int)sizeof(cmd)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp = _popen(cmd, "r");
|
||||
if (!fp) {
|
||||
windows_errno(GetLastError());
|
||||
log_error(errno, "Failed to run PowerShell command '%s' (from popen).\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (out && out_size > 0) {
|
||||
/* get the first line */
|
||||
if (!fgets(out, (int)out_size, fp)) {
|
||||
out[0] = 0;
|
||||
} else {
|
||||
/* trim spaces and newlines */
|
||||
strtrim(out);
|
||||
}
|
||||
}
|
||||
|
||||
ret = _pclose(fp);
|
||||
if (ret == -1) {
|
||||
errno = EINVAL;
|
||||
log_error(errno, "Failed to run PowerShell command '%s' (from pclose).\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
if (ret != 0) {
|
||||
errno = EINVAL;
|
||||
log_error(errno, "PowerShell command '%s' failed with '%d' (from pclose).\n", cmd, ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a GUID string of the form:
|
||||
* xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
* Does NOT accept braced forms like {xxxxxxxx-...}.
|
||||
*
|
||||
* Returns true if valid, false otherwise.
|
||||
*/
|
||||
int windows_guid_is_valid(const char* guid, int len)
|
||||
{
|
||||
if (!guid || len != 38)
|
||||
return -1;
|
||||
|
||||
if (guid[0] != '{' || guid[37] != '}')
|
||||
return -1;
|
||||
|
||||
const char* inner = guid + 1;
|
||||
|
||||
for (int i = 0; i < 36; ++i) {
|
||||
if (i == 8 || i == 13 || i == 18 || i == 23) {
|
||||
if (inner[i] != '-')
|
||||
return -1;
|
||||
} else {
|
||||
char c = inner[i];
|
||||
int is_digit = (c >= '0' && c <= '9');
|
||||
int is_upper_hex = (c >= 'A' && c <= 'F');
|
||||
int is_lower_hex = (c >= 'a' && c <= 'f');
|
||||
|
||||
if (!is_digit && !is_upper_hex && !is_lower_hex)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int windows_read_guid(const char* guid_link, char* guid, size_t guid_size)
|
||||
{
|
||||
int len = windows_readlink(guid_link, guid, guid_size);
|
||||
if (len < 0 && errno == ENOENT) {
|
||||
guid[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
if (len < 0) {
|
||||
log_error(errno, "Error readlink '%s'. %s.\n", guid_link, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t)len >= guid_size) {
|
||||
errno = ENAMETOOLONG;
|
||||
log_error(errno, "GUID too long in '%s'.\n", guid_link);
|
||||
return -1;
|
||||
}
|
||||
|
||||
guid[len] = 0;
|
||||
|
||||
/* validate that the GUID contains only safe characters: [A-Za-z0-9-{}] */
|
||||
if (windows_guid_is_valid(guid, len) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int windows_rebuild_link(const struct fssnapshot_struct* fss, const char* name)
|
||||
{
|
||||
char cmd[PS_CMD_MAX];
|
||||
char device_path[PATH_MAX];
|
||||
char target_path[PATH_MAX];
|
||||
char link_path[PATH_MAX];
|
||||
char guid_link[PATH_MAX];
|
||||
char guid[PATH_MAX];
|
||||
|
||||
pathcpy(guid_link, sizeof(guid_link), fss->snapshot_dir);
|
||||
pathcat(guid_link, sizeof(guid_link), name);
|
||||
pathcat(guid_link, sizeof(guid_link), SNAPSHOT_GUID);
|
||||
|
||||
pathcpy(link_path, sizeof(link_path), fss->snapshot_dir);
|
||||
pathcat(link_path, sizeof(link_path), name);
|
||||
|
||||
int ret = windows_read_guid(guid_link, guid, sizeof(guid));
|
||||
if (ret != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if no GUID, assume it absent */
|
||||
if (guid[0] == 0) {
|
||||
/* remove stale links */
|
||||
windows_rmdir(link_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"(Get-WmiObject Win32_ShadowCopy | Where-Object {$_.ID -eq '%s'}).DeviceObject",
|
||||
guid);
|
||||
|
||||
if (windows_ps(cmd, device_path, sizeof(device_path)) != 0) {
|
||||
log_error(errno, "Error getting DeviceObject from GUID '%s'. %s.\n", guid, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if no device path, assume snapshot absent */
|
||||
if (device_path[0] == 0) {
|
||||
/* remove stale links */
|
||||
windows_rmdir(guid_link);
|
||||
windows_rmdir(link_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pathimport(target_path, sizeof(target_path), device_path);
|
||||
pathslash(target_path, sizeof(target_path));
|
||||
|
||||
/* remove stale link if present */
|
||||
windows_rmdir(link_path);
|
||||
|
||||
if (windows_symlink_directory(target_path, link_path) != 0) {
|
||||
log_error(errno, "Error creating symlink '%s'. %s.\n", link_path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int windows_delete_link(const struct fssnapshot_struct* fss, const char* name)
|
||||
{
|
||||
char link_path[PATH_MAX];
|
||||
|
||||
pathcpy(link_path, sizeof(link_path), fss->snapshot_dir);
|
||||
pathcat(link_path, sizeof(link_path), name);
|
||||
|
||||
/* remove link if present */
|
||||
windows_rmdir(link_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fssnapshot_mount(const char* dir, struct fssnapshot_struct* fss)
|
||||
{
|
||||
wchar_t conv_buf_vol[CONV_MAX];
|
||||
char conv_buf_root[CONV_MAX];
|
||||
wchar_t volume_root[PATH_MAX];
|
||||
wchar_t volume_name[PATH_MAX];
|
||||
wchar_t fs_name[32];
|
||||
uint32_t magic;
|
||||
|
||||
/*
|
||||
* GetVolumePathNameW() accepts any path: file, directory, or deep
|
||||
* subdirectory, and resolves it to the volume root (e.g. L"C:\").
|
||||
* The result always carries a trailing backslash.
|
||||
*
|
||||
* Use convert_if_required() to avoid the automatic addition of \\?\
|
||||
* made by convert() that is propagated in the resulting volume_root
|
||||
*/
|
||||
if (!GetVolumePathNameW(convert_if_required(conv_buf_vol, dir), volume_root, PATH_MAX)) {
|
||||
windows_errno(GetLastError());
|
||||
log_error(errno, "Error getting VolumeRoot from '%s'. %s.\n", dir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!GetVolumeInformationW(volume_root, 0, 0, 0, 0, 0, fs_name, 32)) {
|
||||
windows_errno(GetLastError());
|
||||
log_error(errno, "Error getting information of VolumeRoot '%s'. %s.\n", u16tou8(conv_buf_root, volume_root), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (wcscmp(fs_name, L"NTFS") == 0)
|
||||
magic = WINDOWS_NTFS_MAGIC;
|
||||
else
|
||||
return -1; /* support only NTFS */
|
||||
|
||||
/*
|
||||
* Obtain the canonical volume name: \\?\Volume{GUID}\
|
||||
*
|
||||
* GetVolumePathNameW() may return a drive letter path ("C:\") or a
|
||||
* directory mount point ("D:\Mount\"). Neither is guaranteed to be
|
||||
* accepted by Win32_ShadowCopy.Create() on volumes without a drive
|
||||
* letter. GetVolumeNameForVolumeMountPointW() always returns the
|
||||
* stable kernel-form GUID path that VSS requires, regardless of how
|
||||
* many (or how few) mount points the volume has.
|
||||
*
|
||||
* We store it in fss->dataset.
|
||||
*/
|
||||
if (!GetVolumeNameForVolumeMountPointW(volume_root, volume_name, PATH_MAX)) {
|
||||
windows_errno(GetLastError());
|
||||
log_error(errno, "Error getting VolumeName of VolumeRoot '%s'. %s.\n", u16tou8(conv_buf_root, volume_root), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* don't use pathimport for dataset to keep backslashes */
|
||||
pathcpy(fss->dataset, sizeof(fss->dataset), u16tou8(conv_buf_root, volume_name));
|
||||
|
||||
/* use pathimport to convert backslashes to slashes */
|
||||
pathimport(fss->root_dir, sizeof(fss->root_dir), u16tou8(conv_buf_root, volume_root));
|
||||
|
||||
/* the returned root_dir should match the start of the passed dir */
|
||||
if (pathncmp(fss->root_dir, dir, strlen(fss->root_dir)) != 0) {
|
||||
errno = EINVAL;
|
||||
log_error(errno, "Not matching VolumeRoot '%s' for '%s'.\n", fss->root_dir, dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pathcpy(fss->snapshot_dir, sizeof(fss->snapshot_dir), fss->root_dir);
|
||||
pathcat(fss->snapshot_dir, sizeof(fss->snapshot_dir), SNAPSHOT_CONTAINER "/");
|
||||
|
||||
if (windows_mkdir(fss->snapshot_dir) != 0 && errno != EEXIST) {
|
||||
log_error(errno, "Error creating directory '%s'. %s.\n", fss->snapshot_dir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* refresh the links, after reboots they gets invalidated */
|
||||
if (windows_rebuild_link(fss, SNAPSHOT_PENDING) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (windows_rebuild_link(fss, SNAPSHOT_STABLE) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fss->magic = magic;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fssnapshot_stat(struct fssnapshot_struct* fss, const char* name, struct stat* st)
|
||||
{
|
||||
char link_path[PATH_MAX];
|
||||
|
||||
pathcpy(link_path, sizeof(link_path), fss->snapshot_dir);
|
||||
pathcat(link_path, sizeof(link_path), name);
|
||||
pathcat(link_path, sizeof(link_path), SNAPSHOT_GUID); /* we check the GUID as it's the one that matter */
|
||||
|
||||
/* use lstat because we want to check the link */
|
||||
if (windows_lstat(link_path, st) != 0)
|
||||
return -1;
|
||||
|
||||
/* it must be a link to a directory */
|
||||
if (!S_ISLNKDIR(st->st_mode))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fssnapshot_create(const struct fssnapshot_struct* fss, const char* name)
|
||||
{
|
||||
char cmd[PS_CMD_MAX];
|
||||
char device_path[PATH_MAX];
|
||||
char target_path[PATH_MAX];
|
||||
char link_path[PATH_MAX];
|
||||
char guid_link[PATH_MAX];
|
||||
char out[PATH_MAX];
|
||||
|
||||
/* create VSS shadow copy and capture the system-assigned GUID */
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"$r = (Get-WmiObject -List Win32_ShadowCopy).Create('%s', 'ClientAccessible'); "
|
||||
"if ($r.ReturnValue -eq 0) { "
|
||||
"Write-Output ('ID_' + $r.ShadowID); "
|
||||
"} else { "
|
||||
"Write-Output ('0x{0:X8}' -f $r.ReturnValue); "
|
||||
"}",
|
||||
fss->dataset);
|
||||
if (windows_ps(cmd, out, sizeof(out)) != 0) {
|
||||
log_error(errno, "Error creating snapshot of VolumeName '%s'. %s.\n", fss->dataset, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (strncmp(out, "ID_", 3) != 0) {
|
||||
errno = ENODATA;
|
||||
log_error(errno, "VSS snapshot creation of '%s' failed with error %s.\n", fss->dataset, out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* guid = out + 3;
|
||||
|
||||
/* resolve the shadow copy's device object path */
|
||||
snprintf(cmd, sizeof(cmd), "(Get-WmiObject Win32_ShadowCopy | Where-Object {$_.ID -eq '%s'}).DeviceObject", guid);
|
||||
if (windows_ps(cmd, device_path, sizeof(device_path)) != 0) {
|
||||
log_error(errno, "Error getting the snapshot DeviceObject from GUID '%s'. %s.\n", guid, strerror(errno));
|
||||
goto bail_and_delete;
|
||||
}
|
||||
if (device_path[0] == 0) {
|
||||
errno = ENODATA;
|
||||
log_error(errno, "Empty snapshot DeviceObject of GUID '%s'. %s.\n", guid, strerror(errno));
|
||||
goto bail_and_delete;
|
||||
}
|
||||
|
||||
/* use pathimport to convert backslashes to slashes */
|
||||
pathimport(target_path, sizeof(target_path), device_path);
|
||||
|
||||
/* the ending slash is required, otherwise the link won't work */
|
||||
pathslash(target_path, sizeof(target_path));
|
||||
|
||||
pathcpy(link_path, sizeof(link_path), fss->snapshot_dir);
|
||||
pathcat(link_path, sizeof(link_path), name);
|
||||
|
||||
/* remove potential stale link */
|
||||
windows_rmdir(link_path);
|
||||
|
||||
int ret = windows_symlink_directory(target_path, link_path);
|
||||
if (ret != 0) {
|
||||
log_error(errno, "Error creating symlink '%s'. %s.\n", link_path, strerror(errno));
|
||||
goto bail_and_delete;
|
||||
}
|
||||
|
||||
pathcpy(guid_link, sizeof(guid_link), fss->snapshot_dir);
|
||||
pathcat(guid_link, sizeof(guid_link), name);
|
||||
pathcat(guid_link, sizeof(guid_link), SNAPSHOT_GUID);
|
||||
|
||||
/* store GUID persistently as symlink target */
|
||||
ret = windows_symlink_directory(guid, guid_link);
|
||||
if (ret != 0) {
|
||||
log_error(errno, "Error creating GUID symlink '%s'. %s.\n", guid_link, strerror(errno));
|
||||
windows_rmdir(link_path);
|
||||
goto bail_and_delete;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bail_and_delete:
|
||||
/* destroy the shadow copy we just created */
|
||||
snprintf(cmd, sizeof(cmd), "Get-WmiObject Win32_ShadowCopy | Where-Object {$_.ID -eq '%s'} | Remove-WmiObject", guid);
|
||||
windows_ps(cmd, 0, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fssnapshot_delete(const struct fssnapshot_struct* fss, const char* name)
|
||||
{
|
||||
char guid[PATH_MAX];
|
||||
char cmd[PS_CMD_MAX];
|
||||
char guid_link[PATH_MAX];
|
||||
|
||||
pathcpy(guid_link, sizeof(guid_link), fss->snapshot_dir);
|
||||
pathcat(guid_link, sizeof(guid_link), name);
|
||||
pathcat(guid_link, sizeof(guid_link), SNAPSHOT_GUID);
|
||||
|
||||
if (windows_read_guid(guid_link, guid, sizeof(guid)) != 0)
|
||||
return -1;
|
||||
|
||||
/* if the GUID link is gone, assume everything is gone */
|
||||
if (guid[0] == 0) {
|
||||
/* remove as not valid anymore */
|
||||
windows_delete_link(fss, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* destroy the VSS shadow copy */
|
||||
snprintf(cmd, sizeof(cmd), "Get-WmiObject Win32_ShadowCopy | Where-Object {$_.ID -eq '%s'} | Remove-WmiObject", guid);
|
||||
int ret = windows_ps(cmd, 0, 0);
|
||||
if (ret != 0) {
|
||||
log_error(errno, "Error destroy the snapshot from GUID '%s'. %s.\n", guid, strerror(errno));
|
||||
/* remove as not valid anymore */
|
||||
windows_delete_link(fss, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* remove live symlink if present */
|
||||
windows_delete_link(fss, name);
|
||||
|
||||
/* remove persistent GUID symlink */
|
||||
if (windows_rmdir(guid_link) != 0) {
|
||||
log_error(errno, "Error rmdir '%s'. %s.\n", guid_link, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fssnapshot_rename(const struct fssnapshot_struct* fss, const char* old_name, const char* new_name)
|
||||
{
|
||||
char old_guid[PATH_MAX];
|
||||
char new_guid[PATH_MAX];
|
||||
|
||||
pathcpy(old_guid, sizeof(old_guid), fss->snapshot_dir);
|
||||
pathcat(old_guid, sizeof(old_guid), old_name);
|
||||
pathcat(old_guid, sizeof(old_guid), SNAPSHOT_GUID);
|
||||
pathcpy(new_guid, sizeof(new_guid), fss->snapshot_dir);
|
||||
pathcat(new_guid, sizeof(new_guid), new_name);
|
||||
pathcat(new_guid, sizeof(new_guid), SNAPSHOT_GUID);
|
||||
|
||||
if (windows_rename(old_guid, new_guid) != 0) {
|
||||
log_error(errno, "Error renaming '%s' to '%s'. %s.\n", old_guid, new_guid, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* the rename is the latest operation, and then we don't care about the links, we just remove both */
|
||||
windows_delete_link(fss, old_name);
|
||||
windows_delete_link(fss, new_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fssnapshot_unmount(const struct fssnapshot_struct* fss)
|
||||
{
|
||||
/*
|
||||
* Remove symlinks on exit to prevent "Dangling Links."
|
||||
* Since \Device\HarddiskVolumeShadowCopyN paths are reassigned by the kernel
|
||||
* at boot, a stale link would either point to a non-existent device or,
|
||||
* worse, a different disk's snapshot.
|
||||
*
|
||||
* Deleting the link ensures system hygiene and prevents users from seeing
|
||||
* "Location not available" errors or incorrect data after a reboot.
|
||||
*/
|
||||
windows_delete_link(fss, SNAPSHOT_PENDING);
|
||||
windows_delete_link(fss, SNAPSHOT_STABLE);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* resolve */
|
||||
|
||||
|
|
@ -425,15 +1017,15 @@ static void devattr_property(HANDLE h, char* serial, char* model, char* inter)
|
|||
case BusTypeiScsi : type = "iSCSI"; break;
|
||||
case BusTypeSas : type = "SAS"; break;
|
||||
case BusTypeSata : type = "SATA"; break;
|
||||
case BusTypeSd : type = "SD"; break;
|
||||
case BusTypeMmc : type = "MMC"; break;
|
||||
case BusTypeSd : type = "SD"; break; /* Secure Digital cards */
|
||||
case BusTypeMmc : type = "MMC"; break; /* NAND Embedded MultiMediaCards (eMMC) */
|
||||
case BusTypeVirtual : type = "Virtual"; break;
|
||||
case BusTypeFileBackedVirtual : type = "Virtual"; break;
|
||||
case BusTypeSpaces : type = "Storage Spaces"; break;
|
||||
case BusTypeNvme : type = "NVMe"; break;
|
||||
case BusTypeSCM : type = "SCM"; break;
|
||||
case BusTypeUfs : type = "UFS"; break;
|
||||
case BusTypeNvmeof : type = "NVMe"; break;
|
||||
case BusTypeNvme : type = "NVMe"; break; /* NAND */
|
||||
case BusTypeSCM : type = "SCM"; break; /* NAND Storage Class Memory */
|
||||
case BusTypeUfs : type = "UFS"; break; /* NAND Universal Flash Storage */
|
||||
case BusTypeNvmeof : type = "NVMe"; break; /* NAND NVMe over Fabrics */
|
||||
default :
|
||||
}
|
||||
if (inter && type) /* always override interface as more detailed than smartctl */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2015 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "stream.h"
|
||||
#include "support.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
|
||||
|
|
|
|||
160
cmdline/os.h
160
cmdline/os.h
|
|
@ -1,160 +0,0 @@
|
|||
// 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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2014 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "search.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "snapraid.h"
|
||||
#include "util.h"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "os.h"
|
||||
#include "snapraid.h"
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
@ -1413,7 +1412,7 @@ int snapraid_main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
|
||||
os_init(opt.force_scan_winfind);
|
||||
os_init(OS_INIT_OPT_AVOID_SLEEP | (opt.force_scan_winfind ? OS_INIT_OPT_WINFIND : 0));
|
||||
app_init();
|
||||
raid_init();
|
||||
crc32c_init();
|
||||
|
|
@ -2091,19 +2090,19 @@ int snapraid_main(int argc, char* argv[])
|
|||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (app_global_interrupt()) {
|
||||
if (os_signal_interrupt()) {
|
||||
/* LCOV_EXCL_START */
|
||||
#ifdef _WIN32
|
||||
exit(STATUS_CONTROL_C_EXIT);
|
||||
#else
|
||||
/* restore default handler */
|
||||
signal(app_global_interrupt(), SIG_DFL);
|
||||
signal(os_signal_interrupt(), SIG_DFL);
|
||||
|
||||
/* raise the signal again*/
|
||||
raise(app_global_interrupt());
|
||||
raise(os_signal_interrupt());
|
||||
|
||||
/* if raise() didn't terminate */
|
||||
_exit(128 + app_global_interrupt());
|
||||
_exit(128 + os_signal_interrupt());
|
||||
#endif
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "snapraid.h"
|
||||
#include "util.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "elem.h"
|
||||
#include "import.h"
|
||||
|
|
@ -5050,7 +5050,7 @@ int state_progress_begin(struct snapraid_state* state, block_off_t blockstart, b
|
|||
return -1;
|
||||
|
||||
/* stop if requested */
|
||||
if (app_global_interrupt()) {
|
||||
if (os_signal_interrupt()) {
|
||||
/* LCOV_EXCL_START */
|
||||
if (!state->opt.gui) {
|
||||
msg_status("Not starting due to interruption\n");
|
||||
|
|
@ -5571,7 +5571,7 @@ int state_progress(struct snapraid_state* state, struct snapraid_io* io, block_o
|
|||
}
|
||||
|
||||
/* stop if requested */
|
||||
if (app_global_interrupt()) {
|
||||
if (os_signal_interrupt()) {
|
||||
/* LCOV_EXCL_START */
|
||||
if (!state->opt.gui) {
|
||||
msg_bar("\n");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "util.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
|
||||
|
|
@ -362,10 +362,18 @@ static void vmsg(FILE* out, const char* format, va_list ap)
|
|||
free(dup);
|
||||
}
|
||||
|
||||
void log_fatal(int err, const char* format, ...)
|
||||
static void msg(FILE* out, const char* format, ...) __attribute__((format(attribute_printf, 2, 3)));
|
||||
static void msg(FILE* out, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vmsg(out, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vlog_fatal(int err, const char* format, va_list ap, const char* post)
|
||||
{
|
||||
lock_msg();
|
||||
|
||||
if (stdlog) {
|
||||
|
|
@ -374,26 +382,41 @@ void log_fatal(int err, const char* format, ...)
|
|||
else
|
||||
fprintf(stdlog, "msg:fatal: ");
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stdlog, format, ap);
|
||||
va_end(ap);
|
||||
va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
vfprintf(stdlog, format, ap_copy);
|
||||
va_end(ap_copy);
|
||||
|
||||
if (post)
|
||||
fprintf(stdlog, "%s", post);
|
||||
|
||||
fflush(stdlog);
|
||||
}
|
||||
|
||||
va_start(ap, format);
|
||||
vmsg(stderr, format, ap);
|
||||
va_end(ap);
|
||||
va_list ap_copy2;
|
||||
va_copy(ap_copy2, ap);
|
||||
vmsg(stderr, format, ap_copy2);
|
||||
va_end(ap_copy2);
|
||||
|
||||
if (post)
|
||||
msg(stderr, "%s", post);
|
||||
|
||||
fflush(stderr);
|
||||
|
||||
unlock_msg();
|
||||
}
|
||||
|
||||
void log_error(int err, const char* format, ...)
|
||||
void log_fatal(int err, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vlog_fatal(err, format, ap, 0);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vlog_error(int err, const char* format, va_list ap, const char* post)
|
||||
{
|
||||
lock_msg();
|
||||
|
||||
if (stdlog) {
|
||||
|
|
@ -402,15 +425,23 @@ void log_error(int err, const char* format, ...)
|
|||
else
|
||||
fprintf(stdlog, "msg:error: ");
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stdlog, format, ap);
|
||||
va_end(ap);
|
||||
va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
vfprintf(stdlog, format, ap_copy);
|
||||
va_end(ap_copy);
|
||||
|
||||
if (post)
|
||||
fprintf(stdlog, "%s", post);
|
||||
|
||||
fflush(stdlog);
|
||||
} else {
|
||||
va_start(ap, format);
|
||||
vmsg(stderr, format, ap);
|
||||
va_end(ap);
|
||||
va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
vmsg(stderr, format, ap_copy);
|
||||
va_end(ap_copy);
|
||||
|
||||
if (post)
|
||||
msg(stderr, "%s", post);
|
||||
|
||||
fflush(stderr);
|
||||
}
|
||||
|
|
@ -418,6 +449,36 @@ void log_error(int err, const char* format, ...)
|
|||
unlock_msg();
|
||||
}
|
||||
|
||||
void log_error(int err, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vlog_error(err, format, ap, 0);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void os_syslog(int level, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
switch (level) {
|
||||
case OS_LVL_CRITICAL :
|
||||
vlog_fatal(ESYSLOG, format, ap, "\n");
|
||||
break;
|
||||
case OS_LVL_ERROR :
|
||||
vlog_error(ESYSLOG, format, ap, "\n");
|
||||
break;
|
||||
case OS_LVL_WARNING :
|
||||
case OS_LVL_INFO :
|
||||
/* do not log */
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void log_expected(int err, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#ifndef __SUPPORT_H
|
||||
#define __SUPPORT_H
|
||||
|
||||
#include "os.h"
|
||||
#include "app.h"
|
||||
|
||||
/****************************************************************************/
|
||||
|
|
@ -93,6 +92,7 @@ typedef void log_ptr(int err, const char* format, ...) __attribute__((format(att
|
|||
*
|
||||
* These messages go in the log file and in stderr unconditionally.
|
||||
*/
|
||||
void vlog_fatal(int err, const char* format, va_list ap, const char* post);
|
||||
void log_fatal(int err, const char* format, ...) __attribute__((format(attribute_printf, 2, 3)));
|
||||
|
||||
/**
|
||||
|
|
@ -106,6 +106,7 @@ void log_fatal(int err, const char* format, ...) __attribute__((format(attribute
|
|||
*
|
||||
* These messages go in the log file if specified, otherwise they go in stderr.
|
||||
*/
|
||||
void vlog_error(int err, const char* format, va_list ap, const char* post);
|
||||
void log_error(int err, const char* format, ...) __attribute__((format(attribute_printf, 2, 3)));
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2025 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "thermal.h"
|
||||
#include "state.h"
|
||||
|
|
@ -328,7 +328,7 @@ void state_thermal_cooldown(struct snapraid_state* state)
|
|||
sleep_time -= 30;
|
||||
}
|
||||
|
||||
if (!app_global_interrupt()) { /* don't wake-up if we are interrupting */
|
||||
if (!os_signal_interrupt()) { /* don't wake-up if we are interrupting */
|
||||
log_tag("thermal:spinup\n");
|
||||
|
||||
/* spinup */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2014 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "elem.h"
|
||||
|
|
|
|||
832
cmdline/unix.c
832
cmdline/unix.c
|
|
@ -1,832 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#ifndef __MINGW32__ /* Only for Unix */
|
||||
|
||||
#include "os.h"
|
||||
#include "support.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* signal */
|
||||
|
||||
void os_signal_restore_after_fork(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
/* do not restore SIGPIPE */
|
||||
|
||||
/* ensure signals are unblocked */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL); /* cannot use pthread_sigmask after fork */
|
||||
}
|
||||
|
||||
void os_signal_init(void (*handler_term)(int sig), void (*handler_hup)(int sig))
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = handler_term;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART; /* use the SA_RESTART to automatically restart interrupted system calls */
|
||||
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
|
||||
sa.sa_handler = handler_hup;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART; /* use the SA_RESTART to automatically restart interrupted system calls */
|
||||
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
|
||||
sa.sa_handler = SIG_IGN; /* ignore the signal */
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGPIPE, &sa, 0);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* unix */
|
||||
|
||||
#if !HAVE_EACCESS
|
||||
int eaccess(const char* pathname, int mode)
|
||||
{
|
||||
return faccessat(AT_FDCWD, pathname, mode, AT_EACCESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_POSIX_FADVISE
|
||||
int posix_fadvise_wrapper(int fd, off_t offset, off_t len, int advice)
|
||||
{
|
||||
int ret = posix_fadvise(fd, offset, len, advice);
|
||||
|
||||
if (ret == ENOENT)
|
||||
return ENOSYS;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int open_noatime(const char* file, int flags)
|
||||
{
|
||||
#ifdef O_NOATIME
|
||||
int f = open(file, flags | O_NOATIME);
|
||||
|
||||
/* only root is allowed to use O_NOATIME, in case retry without it */
|
||||
if (f == -1 && errno == EPERM)
|
||||
f = open(file, flags);
|
||||
return f;
|
||||
#else
|
||||
return open(file, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
int dirent_hidden(struct dirent* dd)
|
||||
{
|
||||
return dd->d_name[0] == '.';
|
||||
}
|
||||
|
||||
size_t direct_size(void)
|
||||
{
|
||||
long size;
|
||||
|
||||
size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
if (size == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal(EEXTERNAL, "No page size\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
const char* stat_desc(struct stat* st)
|
||||
{
|
||||
if (S_ISREG(st->st_mode))
|
||||
return "regular";
|
||||
if (S_ISDIR(st->st_mode))
|
||||
return "directory";
|
||||
if (S_ISCHR(st->st_mode))
|
||||
return "character";
|
||||
if (S_ISBLK(st->st_mode))
|
||||
return "block-device";
|
||||
if (S_ISFIFO(st->st_mode))
|
||||
return "fifo";
|
||||
if (S_ISLNK(st->st_mode))
|
||||
return "symlink";
|
||||
if (S_ISSOCK(st->st_mode))
|
||||
return "socket";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* exec */
|
||||
|
||||
/*
|
||||
* Securely verify and open an executable file.
|
||||
*
|
||||
* Performs a series of security checks on the file at @exec_path before
|
||||
* returning an open file descriptor suitable for use with fexecve(2), or
|
||||
* falling back to execve(2) on systems that lack it.
|
||||
*
|
||||
* Security model:
|
||||
* - @exec_path must be absolute.
|
||||
* - The path is resolved via realpath(3) to canonicalize it and eliminate
|
||||
* symlinks before any further checks are performed.
|
||||
* - The parent directory is opened with O_PATH | O_NOFOLLOW to pin its
|
||||
* inode, and the file is opened with openat(2) relative to that pinned
|
||||
* descriptor. This closes the TOCTOU window between path resolution
|
||||
* and file open.
|
||||
* - Both the parent directory and the file must be owned by root or the
|
||||
* daemon's real/effective UID, must not be world-writable, and must not
|
||||
* be group-writable by a group other than the daemon's real/effective GID.
|
||||
* - The file must be a regular file with at least one execute bit set.
|
||||
* - The setuid and setgid bits must not be set.
|
||||
* - On systems with fexecve(2) support, the returned fd is opened without
|
||||
* O_CLOEXEC if the file is a script (@is_script=1). This is necessary because
|
||||
* fexecve(2) executes scripts by passing a descriptor path (e.g., /dev/fd/N)
|
||||
* to the script interpreter, which requires the fd to remain open across
|
||||
* the execve syscall. For compiled binaries, O_CLOEXEC is safely applied
|
||||
* to prevent leaking the fd to the spawned process. On systems without
|
||||
* fexecve(2), execve(2) is used with @resolved_path and O_CLOEXEC is
|
||||
* always applied.
|
||||
*
|
||||
* @exec_path Absolute path to the executable to verify.
|
||||
* @resolved_path Caller-allocated buffer of at least PATH_MAX bytes. On
|
||||
* success, filled with the canonicalized path from realpath(3).
|
||||
*
|
||||
* Returns an open file descriptor (>= 0) on success. The caller is
|
||||
* responsible for closing it. Returns -1 on any verification failure;
|
||||
* the specific reason is emitted via log_error(...).
|
||||
*/
|
||||
static int verify_executable(const char* exec_path, char* resolved_path, int is_script)
|
||||
{
|
||||
struct stat st;
|
||||
uid_t process_uid, process_euid;
|
||||
gid_t process_gid, process_egid;
|
||||
|
||||
process_uid = getuid();
|
||||
process_euid = geteuid();
|
||||
process_gid = getgid();
|
||||
process_egid = getegid();
|
||||
|
||||
/* verify path is absolute */
|
||||
if (exec_path[0] != '/') {
|
||||
log_error(EINVAL, "Path %s must be absolute", exec_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* resolve the path to prevent symlink attacks */
|
||||
if (!realpath(exec_path, resolved_path)) {
|
||||
log_error(errno, "Failed to resolve %s. %s.", exec_path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
char* last_slash = strrchr(resolved_path, '/');
|
||||
if (last_slash == 0) {
|
||||
log_error(EINVAL, "Relative execution of %s not allowed", resolved_path);
|
||||
return -1;
|
||||
}
|
||||
if (last_slash == resolved_path) {
|
||||
log_error(EINVAL, "Root dir execution of %s not allowed", resolved_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* exec_name = last_slash + 1;
|
||||
if (exec_name[0] == 0) {
|
||||
log_error(EINVAL, "No executable name in %s", exec_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dir_path[PATH_MAX];
|
||||
size_t dir_len = last_slash - resolved_path;
|
||||
memcpy(dir_path, resolved_path, dir_len);
|
||||
dir_path[dir_len] = 0;
|
||||
|
||||
int dir_fd = open(dir_path, O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (dir_fd < 0) {
|
||||
log_error(errno, "Failed to open directory %s. %s.", dir_path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fstat(dir_fd, &st) != 0) {
|
||||
log_error(errno, "Failed to stat directory %s. %s.", dir_path, strerror(errno));
|
||||
close(dir_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* directory must be owned by root or the daemon's real user */
|
||||
if (st.st_uid != process_uid && st.st_uid != process_euid && st.st_uid != 0) {
|
||||
log_error(EINVAL, "Directory %s owner must match the daemon owner or be root", dir_path);
|
||||
close(dir_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* directory must be not group-writable unless group matches daemon */
|
||||
if ((st.st_mode & S_IWGRP) && st.st_gid != process_gid && st.st_gid != process_egid && st.st_gid != 0) {
|
||||
log_error(EINVAL, "Directory %s must be not group-writable unless group matches daemon owner or root", dir_path);
|
||||
close(dir_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* directory must be not world-writable */
|
||||
if (st.st_mode & S_IWOTH) {
|
||||
log_error(EINVAL, "Directory %s must be not world-writable", dir_path);
|
||||
close(dir_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the executable
|
||||
*/
|
||||
int flags = O_RDONLY | O_NOFOLLOW;
|
||||
#if HAVE_FEXECVE
|
||||
if (!is_script) {
|
||||
flags |= O_CLOEXEC; /* with fexecve cannot use O_CLOEXEC (Close on Exec) for scripts */
|
||||
}
|
||||
#else
|
||||
(void)is_script;
|
||||
flags |= O_CLOEXEC;
|
||||
#endif
|
||||
|
||||
int fd = openat(dir_fd, exec_name, flags);
|
||||
if (fd < 0) {
|
||||
log_error(errno, "Failed to open %s. %s.", resolved_path, strerror(errno));
|
||||
close(dir_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(dir_fd);
|
||||
|
||||
/* get the file handle (TOCTOU Protection) */
|
||||
if (fstat(fd, &st) == -1) {
|
||||
log_error(errno, "Failed to stat %s. %s.", resolved_path, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ensure it's a regular file */
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
log_error(EINVAL, "File %s is not a regular file", resolved_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* explicitly reject scripts if not allowed */
|
||||
if (!is_script) {
|
||||
char magic[2];
|
||||
if (read(fd, magic, 2) == 2 && magic[0] == '#' && magic[1] == '!') {
|
||||
log_error(EINVAL, "File %s is a script, which is not supported", resolved_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
|
||||
/* ignore lseek errors on block devices or pipes, though it's a regular file here */
|
||||
}
|
||||
}
|
||||
|
||||
/* ensure it has execute permissions */
|
||||
if (!(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
|
||||
log_error(EINVAL, "File %s is not an executable", resolved_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* must be owned by root or the daemon's real user */
|
||||
if (st.st_uid != process_uid && st.st_uid != process_euid && st.st_uid != 0) {
|
||||
log_error(EINVAL, "File %s owner must match the daemon owner or be root", resolved_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* must be not group-writable unless group matches daemon */
|
||||
if ((st.st_mode & S_IWGRP) && st.st_gid != process_gid && st.st_gid != process_egid && st.st_gid != 0) {
|
||||
log_error(EINVAL, "File %s must be not group-writable unless group matches daemon owner or root", resolved_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* must be not world-writable */
|
||||
if (st.st_mode & S_IWOTH) {
|
||||
log_error(EINVAL, "File %s must be not world-writable", resolved_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* must be not setuid / setgid */
|
||||
if (st.st_mode & (S_ISUID | S_ISGID)) {
|
||||
log_error(EINVAL, "File %s has setuid/setgid bits set", resolved_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int os_validate_exec_input(const char* str)
|
||||
{
|
||||
/* reject paths trying to go up levels */
|
||||
if (strstr(str, "..") != 0)
|
||||
return -1;
|
||||
|
||||
/* reject inputs starting with '-' to prevent Flag Injection */
|
||||
if (str[0] == '-')
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scrubbed environment
|
||||
* Only provide the bare essentials.
|
||||
*/
|
||||
static char* const envp_scrubbed[] = {
|
||||
"PATH="
|
||||
#ifdef __APPLE__
|
||||
"/opt/homebrew/bin:"
|
||||
#endif
|
||||
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"TERM=dumb",
|
||||
"LANG=C",
|
||||
"IFS= \t\n",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int pipe_cloexec(int pipefd[2])
|
||||
{
|
||||
#ifdef HAVE_PIPE2
|
||||
return pipe2(pipefd, O_CLOEXEC);
|
||||
#else
|
||||
if (pipe(pipefd) < 0)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int flags = fcntl(pipefd[i], F_GETFD);
|
||||
if (flags < 0)
|
||||
goto bail;
|
||||
|
||||
if (fcntl(pipefd[i], F_SETFD, flags | FD_CLOEXEC) < 0)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Fork and execute a verified executable, capturing stdout.
|
||||
*
|
||||
* Spawns @argv[0] in a new process with stdout connected to a pipe whose
|
||||
* read end is returned in @stdout_fd. stdin and stderr are redirected to
|
||||
* /dev/null. Use this when the child's error output needs to be read and
|
||||
* processed by the daemon.
|
||||
*
|
||||
* The child is placed in its own process group (setpgid) to isolate it
|
||||
* from signals sent to the daemon's process group. The daemon can terminate
|
||||
* the child and all its descendants with kill(-pid, SIGTERM).
|
||||
*
|
||||
* The pipe is created with O_CLOEXEC on both ends. The write end is closed
|
||||
* in the parent after fork. The read end's buffer is reduced to 4096 bytes
|
||||
* to improve read responsiveness on low-volume output.
|
||||
*
|
||||
* @argv NULL-terminated argument vector. argv[0] must be the absolute
|
||||
* path to the executable.
|
||||
* @stdout_fd On success, set to the read end of the stdout pipe. The caller
|
||||
* is responsible for closing it when done.
|
||||
*
|
||||
* Returns the child PID on success, or -1 on failure.
|
||||
*/
|
||||
static pid_t os_spawn_stdout(const char** argv, int* stdout_fd)
|
||||
{
|
||||
char resolved_path[PATH_MAX];
|
||||
int out_pipe[2];
|
||||
pid_t pid;
|
||||
|
||||
int fd = verify_executable(argv[0], resolved_path, 0);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pipe_cloexec(out_pipe) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
close(out_pipe[0]);
|
||||
close(out_pipe[1]);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* child process */
|
||||
|
||||
/*
|
||||
* Create a new process group for the child.
|
||||
* This isolates the child from signals sent to the daemon's process group
|
||||
* and allows the daemon to kill this process and all its future children
|
||||
* (the entire group) using kill(-pid, SIGTERM).
|
||||
*/
|
||||
setpgid(0, 0);
|
||||
|
||||
/* io sandboxing */
|
||||
int null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
if (null_fd < 0)
|
||||
_exit(126);
|
||||
|
||||
/* stdin -> /dev/null */
|
||||
/* stdout -> pipe */
|
||||
/* stderr -> /dev/null */
|
||||
if (dup2(null_fd, STDIN_FILENO) < 0
|
||||
|| dup2(out_pipe[1], STDOUT_FILENO) < 0
|
||||
|| dup2(null_fd, STDERR_FILENO) < 0)
|
||||
_exit(126);
|
||||
|
||||
close(out_pipe[0]);
|
||||
close(out_pipe[1]);
|
||||
|
||||
/* if the fd we opened is not one of the standard ones, close it */
|
||||
if (null_fd > STDERR_FILENO)
|
||||
close(null_fd);
|
||||
|
||||
#if defined(CLOSE_RANGE_CLOEXEC) && defined(HAVE_CLOSE_RANGE)
|
||||
/*
|
||||
* Set all fd to be closed on exec as extra safety measure
|
||||
*
|
||||
* fallback: if it fails, we assume to be still safe, as all fds and
|
||||
* sockets should be already created with CLOEXEC.
|
||||
*/
|
||||
close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
|
||||
#endif
|
||||
|
||||
/* restore and unblock signals */
|
||||
os_signal_restore_after_fork();
|
||||
|
||||
/* use the resolved path for execution */
|
||||
argv[0] = resolved_path;
|
||||
|
||||
/*
|
||||
* Direct Execution via File Descriptor
|
||||
* The kernel uses the shebang in the FD to find the interpreter.
|
||||
*/
|
||||
#if HAVE_FEXECVE
|
||||
fexecve(fd, (char**)argv, envp_scrubbed);
|
||||
#else
|
||||
/* fallback: unfortunately must use the path */
|
||||
execve(resolved_path, (char**)argv, envp_scrubbed);
|
||||
#endif
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
/* parent */
|
||||
close(fd);
|
||||
|
||||
#ifdef F_SETPIPE_SZ /* not available on macOS */
|
||||
/* set the pipe buffer to the minimum to improve responsiveness */
|
||||
if (fcntl(out_pipe[0], F_SETPIPE_SZ, 4096) == -1) {
|
||||
log_error(errno, "Failed to set pipe size. %s.", strerror(errno));
|
||||
/* log non-fatal error or ignore */
|
||||
}
|
||||
#endif
|
||||
|
||||
close(out_pipe[1]);
|
||||
|
||||
*stdout_fd = out_pipe[0];
|
||||
return pid;
|
||||
}
|
||||
|
||||
static int os_wait(pid_t pid, int* status)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = waitpid(pid, status, 0);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int os_spawn_and_wait(const char** argv)
|
||||
{
|
||||
char resolved_path[PATH_MAX];
|
||||
pid_t pid;
|
||||
|
||||
int fd = verify_executable(argv[0], resolved_path, 0);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
log_error(errno, "Fork failed. %s.", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* child process */
|
||||
/*
|
||||
* Create a new process group for the child.
|
||||
* This isolates the child from signals sent to the daemon's process group
|
||||
* and allows the daemon to kill this process and all its future children
|
||||
* (the entire group) using kill(-pid, SIGTERM).
|
||||
*/
|
||||
setpgid(0, 0);
|
||||
|
||||
/* io sandboxing */
|
||||
int null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
if (null_fd < 0)
|
||||
_exit(126);
|
||||
|
||||
/* stdin -> /dev/null */
|
||||
/* stdout -> /dev/null */
|
||||
/* stderr -> /dev/null */
|
||||
if (dup2(null_fd, STDIN_FILENO) < 0
|
||||
|| dup2(null_fd, STDOUT_FILENO) < 0
|
||||
|| dup2(null_fd, STDERR_FILENO) < 0)
|
||||
_exit(126);
|
||||
|
||||
/* if the fd we opened is not one of the standard ones, close it */
|
||||
if (null_fd > STDERR_FILENO)
|
||||
close(null_fd);
|
||||
|
||||
#if defined(CLOSE_RANGE_CLOEXEC) && defined(HAVE_CLOSE_RANGE)
|
||||
/*
|
||||
* Set all fd to be closed on exec as extra safety measure
|
||||
*
|
||||
* fallback: if it fails, we assume to be still safe, as all fds and
|
||||
* sockets should be already created with CLOEXEC.
|
||||
*/
|
||||
close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
|
||||
#endif
|
||||
|
||||
/* restore and unblock signals */
|
||||
os_signal_restore_after_fork();
|
||||
|
||||
/* use the resolved path for execution */
|
||||
argv[0] = resolved_path;
|
||||
|
||||
/*
|
||||
* Direct Execution via File Descriptor
|
||||
* The kernel uses the shebang in the FD to find the interpreter.
|
||||
*/
|
||||
#if HAVE_FEXECVE
|
||||
fexecve(fd, (char**)argv, envp_scrubbed);
|
||||
#else
|
||||
/* fallback: unfortunately must use the path */
|
||||
execve(resolved_path, (char**)argv, envp_scrubbed);
|
||||
#endif
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
/* parent */
|
||||
close(fd);
|
||||
|
||||
int status;
|
||||
int ret = os_wait(pid, &status);
|
||||
if (ret == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_error(errno, "Wait %s failed. %s.", resolved_path, strerror(errno));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
return WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
return 128 + WTERMSIG(status);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
OS_FILE* os_popen(const char** argv)
|
||||
{
|
||||
int stdout_fd = -1;
|
||||
pid_t pid = os_spawn_stdout(argv, &stdout_fd);
|
||||
if (pid < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE* fp = fdopen(stdout_fd, "r");
|
||||
if (!fp) {
|
||||
/* LCOV_EXCL_START */
|
||||
int saved_errno = errno;
|
||||
close(stdout_fd);
|
||||
int status;
|
||||
os_wait(pid, &status);
|
||||
errno = saved_errno;
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
OS_FILE* os_file = malloc_nofail(sizeof(OS_FILE));
|
||||
|
||||
os_file->fp = fp;
|
||||
os_file->pid = pid;
|
||||
|
||||
return os_file;
|
||||
}
|
||||
|
||||
char* os_fgets(char* s, int size, OS_FILE* stream)
|
||||
{
|
||||
return fgets(s, size, stream->fp);
|
||||
}
|
||||
|
||||
int os_pclose(OS_FILE* stream)
|
||||
{
|
||||
/* leep handles locally to free container block immediately */
|
||||
FILE* fp = stream->fp;
|
||||
pid_t pid = stream->pid;
|
||||
free(stream);
|
||||
|
||||
/* close buffered stream, releasing the underlying fd */
|
||||
fclose(fp);
|
||||
|
||||
int status = 0;
|
||||
if (os_wait(pid, &status) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* os */
|
||||
|
||||
uint64_t os_tick(void)
|
||||
{
|
||||
#if HAVE_MACH_ABSOLUTE_TIME
|
||||
/* for Mac OS X */
|
||||
return mach_absolute_time();
|
||||
#elif HAVE_CLOCK_GETTIME && (defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW))
|
||||
/* for Linux */
|
||||
struct timespec tv;
|
||||
|
||||
/* nanosecond precision with clock_gettime() */
|
||||
#if defined(CLOCK_MONOTONIC_RAW)
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) != 0) {
|
||||
#else
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
|
||||
#endif
|
||||
/* LCOV_EXCL_START */
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return tv.tv_sec * 1000000000ULL + tv.tv_nsec;
|
||||
#else
|
||||
/* other platforms */
|
||||
struct timeval tv;
|
||||
|
||||
/* microsecond precision with gettimeofday() */
|
||||
if (gettimeofday(&tv, 0) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return tv.tv_sec * 1000000ULL + tv.tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t os_tick_ms(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
if (gettimeofday(&tv, 0) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
int os_randomize(void* ptr, size_t size)
|
||||
{
|
||||
int f;
|
||||
ssize_t ret;
|
||||
|
||||
f = open("/dev/urandom", O_RDONLY);
|
||||
if (f == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
ret = read(f, ptr, size);
|
||||
if (ret < 0 || (size_t)ret != size) {
|
||||
/* LCOV_EXCL_START */
|
||||
close(f);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (close(f) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_clear(void)
|
||||
{
|
||||
/* ANSI codes */
|
||||
printf("\033[H"); /* cursor at topleft */
|
||||
printf("\033[2J"); /* clear screen */
|
||||
}
|
||||
|
||||
/* LCOV_EXCL_START */
|
||||
void os_abort(void)
|
||||
{
|
||||
#if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
|
||||
void* stack[32];
|
||||
char** messages;
|
||||
unsigned size;
|
||||
unsigned i;
|
||||
#endif
|
||||
|
||||
printf("Stacktrace of " PACKAGE " v" VERSION);
|
||||
#ifdef _linux
|
||||
printf(", linux");
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
printf(", gcc " __VERSION__);
|
||||
#endif
|
||||
printf(", %d-bit", (int)sizeof(void*) * 8);
|
||||
printf(", PATH_MAX=%d", PATH_MAX);
|
||||
printf("\n");
|
||||
|
||||
#if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
|
||||
size = backtrace(stack, 32);
|
||||
|
||||
messages = backtrace_symbols(stack, size);
|
||||
|
||||
for (i = 1; i < size; ++i) {
|
||||
const char* msg;
|
||||
|
||||
if (messages)
|
||||
msg = messages[i];
|
||||
else
|
||||
msg = "<unknown>";
|
||||
|
||||
printf("[bt] %02u: %s\n", i, msg);
|
||||
|
||||
if (messages) {
|
||||
int ret;
|
||||
char addr2line[1024];
|
||||
size_t j = 0;
|
||||
while (msg[j] != '(' && msg[j] != ' ' && msg[j] != 0)
|
||||
++j;
|
||||
|
||||
snprintf(addr2line, sizeof(addr2line), "addr2line %p -e %.*s", stack[i], (unsigned)j, msg);
|
||||
|
||||
ret = system(addr2line);
|
||||
if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0)
|
||||
printf("exit:%d\n", WEXITSTATUS(ret));
|
||||
if (WIFSIGNALED(ret))
|
||||
printf("signal:%d\n", WTERMSIG(ret));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("Please report this error to the SnapRAID Issues:\n");
|
||||
printf("https://github.com/amadvance/snapraid/issues\n");
|
||||
|
||||
abort();
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
||||
void os_init(int opt)
|
||||
{
|
||||
(void)opt;
|
||||
}
|
||||
|
||||
void os_done(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2013 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#ifndef __MINGW32__ /* Only for Unix */
|
||||
|
||||
#include "os.h"
|
||||
#include "app.h"
|
||||
#include "support.h"
|
||||
|
||||
/****************************************************************************/
|
||||
|
|
@ -23,7 +23,7 @@ int exit_sync_needed = 2;
|
|||
|
||||
volatile int global_interrupt = 0;
|
||||
|
||||
int app_global_interrupt(void)
|
||||
int os_signal_interrupt(void)
|
||||
{
|
||||
return global_interrupt;
|
||||
}
|
||||
|
|
@ -1295,7 +1295,6 @@ static int devuuid_blkid(uint64_t device, char* uuid, size_t uuid_size)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Get the LABEL using libblkid.
|
||||
* It uses a cache to work without root permission, resulting in LABEL
|
||||
|
|
@ -1594,118 +1593,6 @@ int devuuid(uint64_t device_id, const char* device_path, char* uuid, size_t uuid
|
|||
/****************************************************************************/
|
||||
/* fs */
|
||||
|
||||
int filephy(const char* path, uint64_t size, uint64_t* physical)
|
||||
{
|
||||
#if HAVE_LINUX_FIEMAP_H
|
||||
/*
|
||||
* In Linux get the real physical address of the file
|
||||
* Note that FIEMAP doesn't require root permission
|
||||
*/
|
||||
int f;
|
||||
struct fiemap* fiemap;
|
||||
size_t fiemap_size;
|
||||
unsigned int blknum;
|
||||
|
||||
f = open(path, O_RDONLY);
|
||||
if (f == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/*
|
||||
* First try with FIEMAP
|
||||
* if works for ext2, ext3, ext4, xfs, btrfs
|
||||
*/
|
||||
fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
|
||||
fiemap = malloc_nofail(fiemap_size);
|
||||
memset(fiemap, 0, fiemap_size);
|
||||
fiemap->fm_start = 0;
|
||||
fiemap->fm_length = ~0ULL;
|
||||
fiemap->fm_flags = FIEMAP_FLAG_SYNC; /* required to ensure that just created files report a valid address and not 0 */
|
||||
fiemap->fm_extent_count = 1; /* we are interested only at the first block */
|
||||
|
||||
if (ioctl(f, FS_IOC_FIEMAP, fiemap) != -1) {
|
||||
uint32_t flags = fiemap->fm_extents[0].fe_flags;
|
||||
uint64_t offset = fiemap->fm_extents[0].fe_physical;
|
||||
|
||||
/* check some condition for validating the offset */
|
||||
if (flags & FIEMAP_EXTENT_DATA_INLINE) {
|
||||
/* if the data is inline, we don't have an offset to report */
|
||||
*physical = FILEPHY_WITHOUT_OFFSET;
|
||||
} else if (flags & FIEMAP_EXTENT_UNKNOWN) {
|
||||
/* if the offset is unknown, we don't have an offset to report */
|
||||
*physical = FILEPHY_WITHOUT_OFFSET;
|
||||
} else if (offset == 0) {
|
||||
/*
|
||||
* 0 is the general fallback for file-systems when
|
||||
* they don't have an offset to report
|
||||
*/
|
||||
*physical = FILEPHY_WITHOUT_OFFSET;
|
||||
} else {
|
||||
/* finally report the real offset */
|
||||
*physical = offset + FILEPHY_REAL_OFFSET;
|
||||
}
|
||||
|
||||
free(fiemap);
|
||||
|
||||
if (close(f) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(fiemap);
|
||||
|
||||
/* if the file is empty, FIBMAP doesn't work, and we don't even try to use it */
|
||||
if (size == 0) {
|
||||
*physical = FILEPHY_WITHOUT_OFFSET;
|
||||
if (close(f) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Then try with FIBMAP
|
||||
* it works for jfs, reiserfs, ntfs-3g
|
||||
* in exfat it always returns 0, that it's anyway better than the fake inodes
|
||||
*/
|
||||
blknum = 0; /* first block */
|
||||
if (ioctl(f, FIBMAP, &blknum) != -1) {
|
||||
*physical = blknum + FILEPHY_REAL_OFFSET;
|
||||
if (close(f) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise don't use anything, and keep the directory traversal order
|
||||
* at now this should happen only for vfat
|
||||
* and it's surely better than using fake inodes
|
||||
*/
|
||||
*physical = FILEPHY_UNREPORTED_OFFSET;
|
||||
if (close(f) == -1)
|
||||
return -1;
|
||||
#else
|
||||
/*
|
||||
* In a generic Unix use a dummy value for all the files
|
||||
* We don't want to risk to use the inode without knowing
|
||||
* if it really improves performance.
|
||||
* In this way we keep them in the directory traversal order
|
||||
* that at least keeps files in the same directory together.
|
||||
* Note also that in newer file-system with snapshot, like ZFS,
|
||||
* the inode doesn't represent even more the disk position, because files
|
||||
* are not overwritten in place, but rewritten in another location
|
||||
* of the disk.
|
||||
*/
|
||||
*physical = FILEPHY_UNREPORTED_OFFSET;
|
||||
|
||||
(void)path; /* not used here */
|
||||
(void)size;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* from man statfs */
|
||||
#define ADFS_SUPER_MAGIC 0xadf5
|
||||
#define AFFS_SUPER_MAGIC 0xADFF
|
||||
|
|
@ -1800,6 +1687,9 @@ int filephy(const char* path, uint64_t size, uint64_t* physical)
|
|||
#define UFS_BYTESWAPPED_SUPER_MAGIC 0x54190100
|
||||
#define VMHGFS_SUPER_MAGIC 0xBACBACBC
|
||||
#define VZFS_SUPER_MAGIC 0x565A4653
|
||||
#define BTRFS_SUPER_MAGIC 0x9123683E
|
||||
#define BCACHEFS_SUPER_MAGIC 0xCA451A4E
|
||||
#define ZFS_SUPER_MAGIC 0x2FC12FC1
|
||||
|
||||
struct filesystem_entry {
|
||||
unsigned id;
|
||||
|
|
@ -2035,6 +1925,9 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har
|
|||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* snapshot */
|
||||
|
||||
#if HAVE_LINUX_DEVICE
|
||||
static int fssnapshot_inode(const char* path, uint32_t magic, uint64_t root_inode, struct fssnapshot_struct* fss)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
|
||||
#include "portable.h"
|
||||
#include "os/portable.h"
|
||||
|
||||
#include "support.h"
|
||||
#include "util.h"
|
||||
|
|
|
|||
|
|
@ -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/portable.h])
|
||||
AC_CONFIG_SRCDIR([os/os.h])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl Enables as many system-specific extensions as reasonably possible
|
||||
|
|
@ -72,7 +72,7 @@ AC_HEADER_DIRENT
|
|||
AC_HEADER_SYS_WAIT
|
||||
AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h limits.h time.h sys/time.h])
|
||||
AC_CHECK_HEADERS([unistd.h getopt.h io.h inttypes.h byteswap.h])
|
||||
AC_CHECK_HEADERS([pthread.h math.h])
|
||||
AC_CHECK_HEADERS([pthread.h math.h syslog.h grp.h pwd.h])
|
||||
AC_CHECK_HEADERS([sys/file.h sys/ioctl.h sys/sysmacros.h sys/mkdev.h sys/mount.h])
|
||||
AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/btrfs.h execinfo.h])
|
||||
AC_CHECK_HEADERS([mach/mach_time.h sys/sysctl.h])
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
// Copyright (C) 2026 Andrea Mazzoleni
|
||||
|
||||
#ifndef __PORTABLE_MINGW_H
|
||||
#define __PORTABLE_MINGW_H
|
||||
|
|
@ -8,6 +8,14 @@
|
|||
|
||||
#include <wchar.h>
|
||||
|
||||
#define SYSLOG "EventLog"
|
||||
|
||||
#define OS_LVL_CRITICAL 1
|
||||
#define OS_LVL_ERROR 2
|
||||
#define OS_LVL_WARNING 3
|
||||
#define OS_LVL_INFO 4
|
||||
#define OS_LVL_DEBUG 5
|
||||
|
||||
/* map Windows name to POSIX name */
|
||||
#define strncasecmp _strnicmp
|
||||
|
||||
|
|
@ -39,13 +47,44 @@ 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)
|
||||
|
||||
/**
|
||||
* Convert a generic string from UTF16 to UTF8.
|
||||
* \param conv_buf Destination UTF-8 buffer of size CONV_MAX.
|
||||
* \param src Source UTF-16 null-terminated string.
|
||||
* \return The converted string. It never fails.
|
||||
*/
|
||||
char* u16tou8(char* conv_buf, const wchar_t* src);
|
||||
|
||||
/**
|
||||
* Convert a generic string from UTF8 to UTF16.
|
||||
* \param conv_buf Destination UTF-16 buffer of size CONV_MAX.
|
||||
* \param src Source UTF-8 null-terminated string.
|
||||
* \return The converted string. It never fails.
|
||||
*/
|
||||
wchar_t* u8tou16(wchar_t* conv_buf, const char* src);
|
||||
|
||||
/**
|
||||
* Convert a generic string from UTF16 to UTF8.
|
||||
* \param conv_buf Destination UTF-8 buffer.
|
||||
* \param number_of_char Size of the destination buffer in bytes.
|
||||
* \param src Source UTF-16 string.
|
||||
* \param number_of_wchar Number of wide characters to convert.
|
||||
* \param result_length_without_terminator Optional pointer to receive the converted length.
|
||||
* \return The converted string, or 0 on failure.
|
||||
*/
|
||||
char* u16tou8_mayfail(char* conv_buf, size_t number_of_char, const wchar_t* src, size_t number_of_wchar, size_t* result_length_without_terminator);
|
||||
|
||||
/**
|
||||
* Convert a generic string from UTF8 to UTF16.
|
||||
* \param conv_buf Destination UTF-16 buffer.
|
||||
* \param conv_size Size of the destination buffer in wchar_t elements.
|
||||
* \param src Source UTF-8 string.
|
||||
* \param number_of_char Number of bytes to convert (or -1 for null-terminated string).
|
||||
* \param result_length_without_terminator Optional pointer to receive the converted length.
|
||||
* \return The converted string, or 0 on failure.
|
||||
*/
|
||||
wchar_t* u8tou16_mayfail(wchar_t* conv_buf, size_t number_of_wchar, const char* src, size_t number_of_char, size_t* result_length_without_terminator);
|
||||
|
||||
/****************************************************************************/
|
||||
/* file */
|
||||
|
||||
|
|
@ -58,6 +97,8 @@ wchar_t* u8tou16(wchar_t* conv_buf, const char* src);
|
|||
/* Remap functions and types */
|
||||
#undef fopen
|
||||
#define fopen windows_fopen
|
||||
#define FOPEN_TEXT "t"
|
||||
#define FOPEN_BINARY "b"
|
||||
#undef open
|
||||
#define open windows_open
|
||||
#define open_noatime windows_open
|
||||
|
|
@ -106,6 +147,7 @@ wchar_t* u8tou16(wchar_t* conv_buf, const char* src);
|
|||
#undef utimensat
|
||||
#define utimensat windows_utimensat
|
||||
#define O_NOFOLLOW 0
|
||||
#define O_CLOEXEC 0
|
||||
#define dirent_hidden windows_dirent_hidden
|
||||
#define HAVE_STRUCT_DIRENT_D_STAT 1
|
||||
#undef HAVE_STRUCT_DIRENT_D_INO
|
||||
|
|
@ -145,6 +187,12 @@ wchar_t* u8tou16(wchar_t* conv_buf, const char* src);
|
|||
#define HAVE_DIRECT_IO 1
|
||||
#define O_DIRECT 0x10000000
|
||||
#define O_DSYNC 0x20000000
|
||||
#undef gmtime_r
|
||||
#define gmtime_r windows_gmtime_r
|
||||
#undef localtime_r
|
||||
#define localtime_r windows_localtime_r
|
||||
#undef realpath
|
||||
#define realpath windows_realpath
|
||||
|
||||
/**
|
||||
* If nanoseconds are not supported, we report the special STAT_NSEC_INVALID value,
|
||||
|
|
@ -155,6 +203,39 @@ wchar_t* u8tou16(wchar_t* conv_buf, const char* src);
|
|||
/* We have nano second support */
|
||||
#define STAT_NSEC(st) ((int)(st)->st_mtimensec)
|
||||
|
||||
static inline int WEXITSTATUS(DWORD status)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int WTERMSIG(DWORD status)
|
||||
{
|
||||
/* identify most common crash reasons */
|
||||
switch (status) {
|
||||
case 0x40000015 : return SIGABRT; /* STATUS_FATAL_APP_EXIT */
|
||||
case 0xC0000005 : return SIGSEGV; /* STATUS_ACCESS_VIOLATION */
|
||||
case 0xC0000006 : return SIGSEGV; /* STATUS_IN_PAGE_ERROR */
|
||||
case 0xC000001D : return SIGILL; /* STATUS_ILLEGAL_INSTRUCTION */
|
||||
case 0xC00000FD : return SIGSEGV; /* STATUS_STACK_OVERFLOW */
|
||||
case 0xC0000090 : return SIGFPE; /* STATUS_FLOAT_INVALID_OPERATION */
|
||||
case 0xC0000094 : return SIGFPE; /* STATUS_INTEGER_DIVIDE_BY_ZERO */
|
||||
case 0xC000013A : return SIGINT; /* STATUS_CONTROL_C_EXIT */
|
||||
case 0xC0000409 : return SIGSEGV; /* STATUS_STACK_BUFFER_OVERRUN */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int WIFEXITED(DWORD status)
|
||||
{
|
||||
return status < 0xC0000000 && WTERMSIG(status) == 0;
|
||||
}
|
||||
|
||||
static inline int WIFSIGNALED(DWORD status)
|
||||
{
|
||||
return WTERMSIG(status) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic stat information.
|
||||
*/
|
||||
|
|
@ -354,7 +435,7 @@ const char* windows_stat_desc(struct stat* st);
|
|||
unsigned windows_sleep(unsigned seconds);
|
||||
|
||||
/**
|
||||
* List usleep().
|
||||
* Like usleep().
|
||||
*/
|
||||
void windows_usleep(uint64_t useconds);
|
||||
|
||||
|
|
@ -411,10 +492,25 @@ ssize_t windows_pread(int f, void* buffer, size_t size, off_t offset);
|
|||
ssize_t windows_pwrite(int f, const void* buffer, size_t size, off_t offset);
|
||||
|
||||
/**
|
||||
* List direct_size().
|
||||
* Like direct_size().
|
||||
*/
|
||||
size_t windows_direct_size(void);
|
||||
|
||||
/**
|
||||
* Like gmtime_r().
|
||||
*/
|
||||
struct tm* windows_gmtime_r(const time_t* timer, struct tm* result);
|
||||
|
||||
/**
|
||||
* Like localtime_r().
|
||||
*/
|
||||
struct tm* windows_localtime_r(const time_t* timer, struct tm* result);
|
||||
|
||||
/**
|
||||
* Like realpath().
|
||||
*/
|
||||
char* windows_realpath(const char* path, char* resolved_path);
|
||||
|
||||
/**
|
||||
* Like GetFileAttributes()
|
||||
*/
|
||||
|
|
@ -448,17 +544,28 @@ int windows_is_wine(void);
|
|||
#define HAVE_THREAD 1
|
||||
typedef void* windows_thread_t;
|
||||
typedef CRITICAL_SECTION windows_mutex_t;
|
||||
typedef struct {
|
||||
SRWLOCK srw;
|
||||
BOOL exclusive;
|
||||
} windows_rwlock_t;
|
||||
typedef CONDITION_VARIABLE windows_cond_t;
|
||||
typedef void* windows_key_t;
|
||||
/* remap to pthread */
|
||||
#define pid_t intptr_t
|
||||
#define thread_id_t windows_thread_t
|
||||
#define thread_mutex_t windows_mutex_t
|
||||
#define thread_cond_t windows_cond_t
|
||||
#define thread_rwlock_t windows_rwlock_t
|
||||
#define thread_key_t windows_key_t
|
||||
#define pthread_mutex_init windows_mutex_init
|
||||
#define pthread_mutex_destroy windows_mutex_destroy
|
||||
#define pthread_mutex_lock windows_mutex_lock
|
||||
#define pthread_mutex_unlock windows_mutex_unlock
|
||||
#define pthread_rwlock_init windows_rwlock_init
|
||||
#define pthread_rwlock_destroy windows_rwlock_destroy
|
||||
#define pthread_rwlock_rdlock windows_rwlock_rdlock
|
||||
#define pthread_rwlock_wrlock windows_rwlock_wrlock
|
||||
#define pthread_rwlock_unlock windows_rwlock_unlock
|
||||
#define pthread_cond_init windows_cond_init
|
||||
#define pthread_cond_destroy windows_cond_destroy
|
||||
#define pthread_cond_signal windows_cond_signal
|
||||
|
|
@ -478,6 +585,11 @@ int windows_mutex_init(windows_mutex_t* mutex, void* attr);
|
|||
int windows_mutex_destroy(windows_mutex_t* mutex);
|
||||
int windows_mutex_lock(windows_mutex_t* mutex);
|
||||
int windows_mutex_unlock(windows_mutex_t* mutex);
|
||||
int windows_rwlock_init(windows_rwlock_t* mutex, void* attr);
|
||||
int windows_rwlock_destroy(windows_rwlock_t* mutex);
|
||||
int windows_rwlock_rdlock(windows_rwlock_t* mutex);
|
||||
int windows_rwlock_wrlock(windows_rwlock_t* mutex);
|
||||
int windows_rwlock_unlock(windows_rwlock_t* mutex);
|
||||
int windows_cond_init(windows_cond_t* cond, void* attr);
|
||||
int windows_cond_destroy(windows_cond_t* cond);
|
||||
int windows_cond_signal(windows_cond_t* cond);
|
||||
|
|
@ -496,6 +608,23 @@ int windows_key_delete(windows_key_t key);
|
|||
void* windows_getspecific(windows_key_t key);
|
||||
int windows_setspecific(windows_key_t key, void* value);
|
||||
|
||||
/****************************************************************************/
|
||||
/* service */
|
||||
|
||||
#if !HAVE_GETOPT_LONG
|
||||
#error HAVE_GETOPT_LONG is required in Windows for the service options
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Log into Eventlog
|
||||
*/
|
||||
void windows_eventlog(int level, const char* msg);
|
||||
|
||||
/**
|
||||
* Signal that the service is starting and it needs more time.
|
||||
*/
|
||||
void windows_starting(void);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
319
os/os.h
Normal file
319
os/os.h
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2026 Andrea Mazzoleni
|
||||
|
||||
#ifndef __OS_H
|
||||
#define __OS_H
|
||||
|
||||
/****************************************************************************/
|
||||
/* syslog */
|
||||
|
||||
/**
|
||||
* Log an informational syslog message.
|
||||
* The message should start with a lowercase letter and must not terminate with \n.
|
||||
* \param level One of OS_LVL_*.
|
||||
* \param format Printf-like format string.
|
||||
*/
|
||||
void os_syslog(int level, const char* format, ...) __attribute__((format(attribute_printf, 2, 3)));
|
||||
|
||||
/****************************************************************************/
|
||||
/* signal */
|
||||
|
||||
/**
|
||||
* Enable or disable signal handling.
|
||||
* \param enable 1 to enable signals, 0 to disable.
|
||||
*/
|
||||
void os_signal_set(int enable);
|
||||
|
||||
/**
|
||||
* Initialize signal handling.
|
||||
* \param handler_term Signal handler callback for termination signals (SIGTERM, SIGINT).
|
||||
* \param handler_hup Signal handler callback for hangup signal (SIGHUP).
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Get string representation of signal number.
|
||||
* \param sig Signal number.
|
||||
* \return Signal name string.
|
||||
*/
|
||||
const char* os_signal_name(int sig);
|
||||
|
||||
/**
|
||||
* Global variable to identify if Ctrl+C is pressed.
|
||||
* \return 1 if Ctrl+C was pressed/interrupted, 0 otherwise.
|
||||
*/
|
||||
int os_signal_interrupt(void);
|
||||
|
||||
/****************************************************************************/
|
||||
/* exec */
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* \return A pointer to an initialized OS_FILE structure on success, or NULL on failure.
|
||||
*/
|
||||
OS_FILE* os_popen(const char** argv);
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
int os_pclose(OS_FILE* stream);
|
||||
|
||||
/**
|
||||
* Spawn a new process with the specified argument vector, optionally capturing stdout and/or stderr.
|
||||
* \param argv Array of command line arguments.
|
||||
* \param stdout_read_fd Pointer to store file descriptor for stdout, or NULL to redirect to /dev/null.
|
||||
* \param stderr_read_fd Pointer to store file descriptor for stderr, or NULL to redirect to /dev/null.
|
||||
* \param run_as_user User to run script as (NULL for current user).
|
||||
* \return Process ID of spawned process, or -1 on failure.
|
||||
*/
|
||||
pid_t os_spawn(char** argv, int* stdout_read_fd, int* stderr_read_fd, const char* run_as_user);
|
||||
|
||||
/**
|
||||
* Wait for the child process to terminate.
|
||||
* \param pid Process ID of the child process.
|
||||
* \param status Pointer to store the exit status.
|
||||
* \return Child PID on success, -1 on failure.
|
||||
*/
|
||||
pid_t os_wait(pid_t pid, int* status);
|
||||
|
||||
/**
|
||||
* Terminate gracefully a process.
|
||||
* \param pid Process ID of the process to terminate.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
int os_term(pid_t pid);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* \param argv NULL-terminated argument vector. argv[0] must be the absolute path
|
||||
* to the executable.
|
||||
* \return The child exit status on success, or -1 on failure.
|
||||
*/
|
||||
int os_spawn_and_wait(const char** argv);
|
||||
|
||||
/**
|
||||
* Execute a system command with optional user context and input.
|
||||
* \param command Command to execute.
|
||||
* \param run_as_user User to run command as (NULL for current user).
|
||||
* \param stdin_text Text to provide as stdin (NULL for no input).
|
||||
* \return Exit status of command.
|
||||
*/
|
||||
int os_command(const char* command, const char* run_as_user, const char* stdin_text);
|
||||
|
||||
/**
|
||||
* Execute a script file with specified user context.
|
||||
* \param argv Array of command line arguments.
|
||||
* \param envp Environment variables (NULL-terminated list of strings).
|
||||
* \param run_as_user User to run script as (NULL for current user).
|
||||
* \return Exit status of script.
|
||||
*/
|
||||
int os_script(char** argv, char** envp, const char* run_as_user);
|
||||
|
||||
/**
|
||||
* Validates a string for exec.
|
||||
* \param str The input string to validate.
|
||||
* \return -1 if dangerous characters are detected, 0 otherwise.
|
||||
*/
|
||||
int os_validate_exec_input(const char* str);
|
||||
|
||||
/****************************************************************************/
|
||||
/* fs */
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* \param path The path of the file.
|
||||
* \param size The size of the file.
|
||||
* \param physical Pointer to store the physical offset.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
int filephy(const char* path, uint64_t size, uint64_t* physical);
|
||||
|
||||
/****************************************************************************/
|
||||
/* privileges */
|
||||
|
||||
/**
|
||||
* Bracketed Privileges System:
|
||||
* These functions allow the daemon to run with dropped effective privileges
|
||||
* by default (euid/egid set to an unprivileged user like "nobody"), while
|
||||
* temporarily escalating to root privileges for specific operations that require
|
||||
* administrative permissions (e.g., config changes, executing SnapRAID commands,
|
||||
* managing log files).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Temporarily acquire root privileges.
|
||||
* Restores the effective user and group ID of the calling thread to root (0).
|
||||
*/
|
||||
void os_privileges_acquire(void);
|
||||
|
||||
/**
|
||||
* Release root privileges.
|
||||
* Reverts the effective user and group ID of the calling thread to the unprivileged credentials.
|
||||
*/
|
||||
void os_privileges_release(void);
|
||||
|
||||
/**
|
||||
* Drop effective privileges permanently to an unprivileged user (e.g., "nobody").
|
||||
* Called after startup/initialization is complete to transition the daemon into
|
||||
* the Bracketed Privileges execution mode.
|
||||
*/
|
||||
void os_privileges_drop(void);
|
||||
|
||||
/****************************************************************************/
|
||||
/* os */
|
||||
|
||||
#define OS_INIT_OPT_AVOID_SLEEP 1 /**< Prevent the system to go to sleep */
|
||||
#define OS_INIT_OPT_WINFIND 2 /**< Use the legacy Windows FindFirstFile interface instead of the new stream interface */
|
||||
|
||||
/**
|
||||
* Initializes the system.
|
||||
* \param opt System initialization options.
|
||||
*/
|
||||
void os_init(unsigned 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.
|
||||
* \return Monotonic clock tick value.
|
||||
*/
|
||||
uint64_t os_tick(void);
|
||||
|
||||
/**
|
||||
* Get the os_tick counter value in millisecond.
|
||||
* \return Monotonic clock value in milliseconds.
|
||||
*/
|
||||
uint64_t os_tick_ms(void);
|
||||
|
||||
/**
|
||||
* Get the os_tick counter value in seconds.
|
||||
* \return Monotonic clock value in seconds.
|
||||
*/
|
||||
uint64_t os_tick_sec(void);
|
||||
|
||||
/**
|
||||
* Abort the process with a stacktrace.
|
||||
*/
|
||||
void os_abort(void) __noreturn;
|
||||
|
||||
/**
|
||||
* Exit the process with failure.
|
||||
*/
|
||||
void os_exit(void) __noreturn;
|
||||
|
||||
/**
|
||||
* Shutdown the system.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
int os_shutdown(void);
|
||||
|
||||
/**
|
||||
* Clear the screen.
|
||||
*/
|
||||
void os_clear(void);
|
||||
|
||||
/**
|
||||
* Fill memory with pseudo-random values
|
||||
* \param ptr Pointer to the memory buffer.
|
||||
* \param size Size of the memory buffer.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
int os_randomize(void* ptr, size_t size);
|
||||
|
||||
/**
|
||||
* Get the optimal CPU for the speed test
|
||||
* \return Optimal CPU index.
|
||||
*/
|
||||
int os_get_optimal_cpu(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
// Copyright (C) 2026 Andrea Mazzoleni
|
||||
|
||||
#ifndef __PORTABLE_H
|
||||
#define __PORTABLE_H
|
||||
|
|
@ -32,6 +32,11 @@
|
|||
*/
|
||||
#define _WIN32_WINNT 0x601
|
||||
|
||||
/**
|
||||
* Enable the rand_s() function.
|
||||
*/
|
||||
#define _CRT_RAND_S
|
||||
|
||||
/**
|
||||
* Undef as it clashes with windows.h declarations
|
||||
*/
|
||||
|
|
@ -46,7 +51,7 @@
|
|||
#ifdef __MINGW32__
|
||||
#if defined(__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO == 1
|
||||
#define attribute_printf gnu_printf /* GNU format */
|
||||
#define printf __mingw_printf
|
||||
#define printf(...) __mingw_printf(__VA_ARGS__) /* to support %z in printf */
|
||||
#else
|
||||
#define attribute_printf ms_printf /* MSVCRT format */
|
||||
#endif
|
||||
|
|
@ -65,7 +70,6 @@
|
|||
#define __noreturn __attribute__((noreturn))
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Architecture for inline assembly.
|
||||
*/
|
||||
|
|
@ -225,6 +229,38 @@
|
|||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRINGS
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYSLOG_H
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_GRP_H
|
||||
#include <grp.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_PWD_H
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_SYSINFO_H
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_UTSNAME_H
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_ZSTD
|
||||
#include <zstd.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <DiskArbitration/DiskArbitration.h>
|
||||
|
|
@ -252,20 +288,6 @@
|
|||
#include <linux/close_range.h>
|
||||
#endif
|
||||
|
||||
/* implement close_range for glibc 2.33 or earlier */
|
||||
#if defined(__linux__) && !defined(HAVE_CLOSE_RANGE)
|
||||
#include <sys/syscall.h>
|
||||
#ifndef __NR_close_range
|
||||
#define __NR_close_range 436
|
||||
#endif
|
||||
#define close_range close_range_impl
|
||||
static inline int close_range_impl(unsigned int first, unsigned int last, unsigned int flags)
|
||||
{
|
||||
return (int)syscall(__NR_close_range, first, last, flags);
|
||||
}
|
||||
#define HAVE_CLOSE_RANGE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Flags for open()
|
||||
*/
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2011 Andrea Mazzoleni
|
||||
// Copyright (C) 2026 Andrea Mazzoleni
|
||||
|
||||
#ifndef __UNIX_H
|
||||
#define __UNIX_H
|
||||
|
|
@ -9,6 +9,38 @@
|
|||
#define HAVE_DIRECT_IO 1 /**< Support O_DIRECT in open(). */
|
||||
#endif
|
||||
|
||||
#define SYSLOG "syslog"
|
||||
|
||||
#define OS_LVL_CRITICAL LOG_CRIT
|
||||
#define OS_LVL_ERROR LOG_ERR
|
||||
#define OS_LVL_WARNING LOG_WARNING
|
||||
#define OS_LVL_INFO LOG_INFO
|
||||
#define OS_LVL_DEBUG LOG_DEBUG
|
||||
|
||||
/* implement close_range for glibc 2.33 or earlier */
|
||||
#if defined(__linux__) && !defined(HAVE_CLOSE_RANGE)
|
||||
#include <sys/syscall.h>
|
||||
#ifndef __NR_close_range
|
||||
#define __NR_close_range 436
|
||||
#endif
|
||||
#define close_range close_range_impl
|
||||
static inline int close_range_impl(unsigned int first, unsigned int last, unsigned int flags)
|
||||
{
|
||||
return (int)syscall(__NR_close_range, first, last, flags);
|
||||
}
|
||||
#define HAVE_CLOSE_RANGE 1
|
||||
#endif
|
||||
|
||||
#ifndef WEXITSTATUS
|
||||
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
|
||||
#endif
|
||||
#ifndef WIFEXITED
|
||||
#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
|
||||
#endif
|
||||
|
||||
#define FOPEN_TEXT ""
|
||||
#define FOPEN_BINARY ""
|
||||
|
||||
#define O_BINARY 0 /**< Not used in Unix. */
|
||||
#define O_SEQUENTIAL 0 /**< In Unix posix_fadvise() shall be used. */
|
||||
|
||||
|
|
@ -119,6 +151,7 @@ typedef pthread_t thread_id_t;
|
|||
typedef pthread_mutex_t thread_mutex_t;
|
||||
typedef pthread_cond_t thread_cond_t;
|
||||
typedef pthread_key_t thread_key_t;
|
||||
typedef pthread_rwlock_t thread_rwlock_t;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
#define tommy_calloc calloc_nofail
|
||||
#define tommy_free free
|
||||
|
||||
#include "cmdline/portable.h"
|
||||
#include "os/portable.h"
|
||||
#include "cmdline/support.h" /* for malloc/calloc_nofail() */
|
||||
|
||||
#include "tommyhash.c"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue