mirror of
https://github.com/Michele-M-Media/HYPERHEN.git
synced 2026-10-23 09:02:25 -05:00
174 lines
4.2 KiB
C
174 lines
4.2 KiB
C
/* Copyright (C) 2025 OnionHEN / LightningMods */
|
|
|
|
#include <onion/fs.h>
|
|
#include <onion/log.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
bool if_exists(const char *path) {
|
|
struct stat buffer;
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
return stat(path, &buffer) == 0;
|
|
}
|
|
|
|
bool touch_file(const char *path) {
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0777);
|
|
if (fd > 0) {
|
|
close(fd);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool write_file_atomic(const char *path, const void *data, size_t size) {
|
|
char temp_path[1024];
|
|
|
|
if (!path || path[0] == '\0' || (size > 0 && !data))
|
|
return false;
|
|
|
|
const int n =
|
|
snprintf(temp_path, sizeof(temp_path), "%s.tmp.%d", path, (int)getpid());
|
|
if (n < 0 || (size_t)n >= sizeof(temp_path))
|
|
return false;
|
|
|
|
unlink(temp_path);
|
|
const int fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0777);
|
|
if (fd < 0) {
|
|
LOG_ERROR("write_file_atomic: open %s failed (%s)", temp_path,
|
|
strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
const uint8_t *cursor = (const uint8_t *)data;
|
|
size_t remaining = size;
|
|
while (remaining > 0) {
|
|
const ssize_t written = write(fd, cursor, remaining);
|
|
if (written < 0 && errno == EINTR)
|
|
continue;
|
|
if (written <= 0) {
|
|
LOG_ERROR("write_file_atomic: write %s failed (%s)", path,
|
|
written < 0 ? strerror(errno) : "zero-byte write");
|
|
close(fd);
|
|
unlink(temp_path);
|
|
return false;
|
|
}
|
|
cursor += (size_t)written;
|
|
remaining -= (size_t)written;
|
|
}
|
|
|
|
if (fsync(fd) != 0) {
|
|
LOG_ERROR("write_file_atomic: fsync %s failed (%s)", path, strerror(errno));
|
|
close(fd);
|
|
unlink(temp_path);
|
|
return false;
|
|
}
|
|
if (close(fd) != 0) {
|
|
LOG_ERROR("write_file_atomic: close %s failed (%s)", path, strerror(errno));
|
|
unlink(temp_path);
|
|
return false;
|
|
}
|
|
if (rename(temp_path, path) != 0) {
|
|
LOG_ERROR("write_file_atomic: rename %s failed (%s)", path, strerror(errno));
|
|
unlink(temp_path);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Read exactly @size bytes into @buf, retrying short reads and EINTR. */
|
|
static bool read_exact(int fd, uint8_t *buf, size_t size) {
|
|
size_t done = 0;
|
|
while (done < size) {
|
|
const ssize_t n = read(fd, buf + done, size - done);
|
|
if (n < 0 && errno == EINTR)
|
|
continue;
|
|
if (n <= 0)
|
|
return false;
|
|
done += (size_t)n;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool read_file_alloc(const char *path, size_t max_size, uint8_t **out,
|
|
size_t *out_size) {
|
|
struct stat st;
|
|
uint8_t *buf;
|
|
size_t size;
|
|
|
|
if (!path || path[0] == '\0' || !out || !out_size)
|
|
return false;
|
|
|
|
const int fd = open(path, O_RDONLY);
|
|
if (fd < 0) {
|
|
LOG_DEBUG("read_file_alloc: open %s failed (%s)", path, strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
if (fstat(fd, &st) != 0) {
|
|
LOG_DEBUG("read_file_alloc: stat %s failed (%s)", path, strerror(errno));
|
|
close(fd);
|
|
return false;
|
|
}
|
|
if (!S_ISREG(st.st_mode) || st.st_size <= 0) {
|
|
LOG_DEBUG("read_file_alloc: %s is not a non-empty regular file", path);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
size = (size_t)st.st_size;
|
|
if (max_size > 0 && size > max_size) {
|
|
LOG_DEBUG("read_file_alloc: %s is %zu bytes, over the %zu byte cap", path,
|
|
size, max_size);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
/* One spare byte so text callers get a C string for free; *out_size stays
|
|
* the file length. */
|
|
if (!(buf = malloc(size + 1))) {
|
|
LOG_DEBUG("read_file_alloc: cannot allocate %zu bytes for %s", size, path);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
if (!read_exact(fd, buf, size)) {
|
|
LOG_DEBUG("read_file_alloc: read %s failed (%s)", path, strerror(errno));
|
|
free(buf);
|
|
close(fd);
|
|
return false;
|
|
}
|
|
close(fd);
|
|
buf[size] = '\0';
|
|
|
|
*out = buf;
|
|
*out_size = size;
|
|
return true;
|
|
}
|
|
|
|
bool read_file_alloc_str(const char *path, size_t max_size, char **out,
|
|
size_t *out_size) {
|
|
uint8_t *bytes = NULL;
|
|
size_t size = 0;
|
|
|
|
if (!out)
|
|
return false;
|
|
|
|
if (!read_file_alloc(path, max_size, &bytes, &size))
|
|
return false;
|
|
|
|
*out = (char *)bytes;
|
|
*out_size = size;
|
|
return true;
|
|
}
|