251 lines
6.2 KiB
C
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;
|
|
}
|