deployd/script/dirs.c
2025-08-02 02:03:28 +03:00

251 lines
6.2 KiB
C

#include "include/conf.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/types.h>
#include "include/dirs.h"
bool sh_dirs_mkparents(const char *path, bool is_parent) {
struct stat dir_stat = {0};
/* Check if directory exists */
if (stat(path, &dir_stat) == 0) {
/* Exists: verify it is a directory */
if (!S_ISDIR(dir_stat.st_mode)) {
sh_print_errorf("%s exists but is not a directory", path);
return false;
}
/* If is not a parent just quit now */
if (!is_parent) {
return true;
}
/* Verify ownership */
if (dir_stat.st_uid != 0) {
sh_print_errorf("%s is not owned by root", path);
return false;
}
/* Fix permissions if more permissive than 0700 */
if ((dir_stat.st_mode & (mode_t)0777) & ~(mode_t)0700) {
sh_print_warnf("%s permissions too permissive, fixing...", path);
if (chmod(path, 0700) != 0) {
perror("chmod");
return false;
}
}
return true;
}
/* If stat failed for reasons other than "not exist", fail */
if (errno != ENOENT) {
sh_print_perrorf("Failed to ensure '%s' exists", path);
return false;
}
/*
* Directory does not exist, create parents first.
*/
char tmp[4096] = {0};
char *ptr = NULL;
size_t len = 0;
strncpy(tmp, path, sizeof(tmp));
tmp[sizeof(tmp) - 1] = '\0';
len = strlen(tmp);
if (len == 0 || len >= sizeof(tmp)) {
sh_print_errorf("Path '%s' too long", path);
return false;
}
/* Strip trailing slashes (but preserve leading '/') */
while (len > 1 && tmp[len - 1] == '/') {
tmp[len - 1] = '\0';
--len;
}
/*
* Recursively create parent directories by iterating through 'tmp'
*/
for (ptr = tmp + 1; *ptr; ++ptr) {
if (*ptr == '/') {
*ptr = '\0'; /* Temporarily terminate string at slash */
if (!sh_dirs_mkparents(tmp, false)) {
return false;
}
*ptr = '/'; /* Restore slash */
}
}
/* Create the final directory */
if (mkdir(tmp, 0700) != 0) {
sh_print_perrorf("mkdir() failed for '%s'", path);
return false;
}
/* Ensure ownership and permissions */
if (chown(tmp, 0, 0) != 0) {
sh_print_perrorf("chown() failed for '%s'", path);
return false;
}
if (chmod(tmp, 0700) != 0) {
sh_print_perrorf("chmod() failed for '%s'", path);
return false;
}
return true;
}
bool sh_dirs_ensure(void) {
return sh_dirs_mkparents(SH_DIRS_CACHE, true) &&
sh_dirs_mkparents(SH_DIRS_DATA, true) &&
sh_dirs_mkparents(SH_DIRS_EXTENSIONS, true);
}
bool sh_dirs_get_ext_path(char out[SH_DIRS_MAX_EXT_PATH], const char *name) {
if (!out || !name || !*name) {
return false;
}
static const size_t ext_len = sizeof(SH_DIRS_EXTENSIONS) - 1;
memcpy(out, SH_DIRS_EXTENSIONS, ext_len);
if (ext_len == 0 || out[ext_len - 1] != '/') {
out[ext_len] = '/';
strcpy(out + ext_len + 1, name);
} else {
strcpy(out + ext_len, name);
}
return true;
}
bool sh_dirs_get_cache_path(char out[SH_DIRS_MAX_DEPLOYS_PATH],
const char *name,
const char *key) {
if (!out || !name) {
return false;
}
dp_Blake2sCtx ctx = {0};
static const size_t ext_len = sizeof(SH_DIRS_DEPLOYS) - 1;
memcpy(out, SH_DIRS_DEPLOYS, ext_len);
if (key) {
const size_t key_len = strlen(key);
if (key_len > 32) {
return false;
}
if (!dp_Blake2sCtx_init(&ctx, DP_BLAKE2S_OUTPUT_SIZE_MAX, key,
(uint8_t)key_len)) {
return false;
}
} else {
if (!dp_Blake2sCtx_init(&ctx, DP_BLAKE2S_OUTPUT_SIZE_MAX, NULL, 0)) {
return false;
}
}
if (!dp_Blake2sCtx_update(&ctx, name, strlen(name))) {
return false;
}
if (!dp_Blake2sCtx_final(&ctx)) {
return false;
}
if (ext_len == 0 || out[ext_len - 1] != '/') {
out[ext_len] = '/';
if (!dp_Blake2sCtx_to_hex(&ctx, out + ext_len + 1)) {
return false;
}
} else {
if (!dp_Blake2sCtx_to_hex(&ctx, out + ext_len)) {
return false;
}
}
return true;
}
int sh_dir_lock(const char *dir) {
if (!dir) {
return -1;
}
dp_Blake2sCtx ctx = {0};
if (!dp_Blake2sCtx_init(&ctx, DP_BLAKE2S_OUTPUT_SIZE_MAX, NULL, 0)) {
return -1;
}
if (!dp_Blake2sCtx_update(&ctx, dir, strlen(dir))) {
return -1;
}
if (!dp_Blake2sCtx_final(&ctx)) {
return -1;
}
char hex_hash[DP_BLAKE2S_OUTPUT_SIZE_MAX_HEX + 1] = {0};
if (!dp_Blake2sCtx_to_hex(&ctx, hex_hash)) {
return -1;
}
static const char prefix[] = "/tmp/.deployd-";
static const char suffix[] = "-lock";
static const size_t prefix_len = sizeof(prefix) - 1;
const size_t hash_len = strlen(hex_hash);
static const size_t suffix_len = sizeof(suffix) - 1;
char path[1024] = {0};
if (prefix_len + hash_len + suffix_len + 1 > sizeof(path)) {
errno = ENAMETOOLONG;
return -1;
}
memcpy(path, prefix, prefix_len);
memcpy(path + prefix_len, hex_hash, hash_len);
memcpy(path + prefix_len + hash_len, suffix, suffix_len);
path[prefix_len + hash_len + suffix_len] = '\0';
const int lockfd = open(path, O_CREAT | O_RDWR, 0600);
if (lockfd < 0) {
return -1;
}
for (uint8_t tries = 0; tries <= 60; ++tries) {
if (flock(lockfd, LOCK_EX) == 0) {
if (!sh_dirs_mkparents(dir, true)) {
flock(lockfd, LOCK_UN);
close(lockfd);
return -1;
}
return lockfd;
}
sleep(1);
}
close(lockfd);
return -1;
}
int sh_dir_unlock(int lock) {
if (lock < 0) {
return -1;
}
if (flock(lock, LOCK_UN) < 0) {
return -1;
}
if (close(lock) < 0) {
return -1;
}
return 0;
}