_ _
| |_ ___| |_ _
| | . | | | |
|_|_|___|_|_ |
|___|
git mirror - github.com/owenewans/holy - branch master
file src/stage.c
#define _POSIX_C_SOURCE 200809L
#include "stage.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/random.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static int temporary_at(int dir, char name[43], int access)
{
unsigned char random_bytes[16];
size_t attempt, done, i;
for (attempt = 0; attempt < 16; ++attempt) {
done = 0;
while (done < sizeof random_bytes) {
ssize_t got = getrandom(random_bytes + done, sizeof random_bytes - done, 0);
if (got < 0 && errno == EINTR) continue;
if (got <= 0) return -1;
done += (size_t)got;
}
memcpy(name, ".holy-tmp-", 10);
for (i = 0; i < sizeof random_bytes; ++i)
snprintf(name + 10 + i * 2, 3, "%02x", random_bytes[i]);
name[42] = '\0';
{
int fd = openat(dir, name, access | O_CREAT | O_EXCL |
O_NOFOLLOW | O_CLOEXEC, 0600);
if (fd >= 0 || errno != EEXIST) return fd;
}
}
errno = EEXIST;
return -1;
}
int holy_temporary_at(int dir, char name[43])
{
return temporary_at(dir, name, O_WRONLY);
}
int holy_spool_at(int dir, char name[43])
{
return temporary_at(dir, name, O_RDWR);
}
char *holy_stage_fd(int input, const char *prefix)
{
const char *base = geteuid() ? getenv("TMPDIR") : NULL;
char *path = NULL;
char buffer[65536];
struct stat st;
int output = -1;
ssize_t got;
size_t length;
off_t offset = 0;
int ok = 0;
if (!base || !*base) base = "/tmp";
if (strlen(base) > (size_t)-1 - strlen(prefix) - sizeof "-XXXXXX" - 1)
return NULL;
length = strlen(base) + strlen(prefix) + sizeof "/-XXXXXX";
path = malloc(length);
if (!path) return NULL;
snprintf(path, length, "%s/%s-XXXXXX", base, prefix);
if (fstat(input, &st) || !S_ISREG(st.st_mode) || st.st_size < 0) goto done;
output = mkstemp(path);
if (output < 0) goto done;
while (offset < st.st_size) {
size_t amount = (uint64_t)(st.st_size - offset) < sizeof buffer ?
(size_t)(st.st_size - offset) : sizeof buffer;
size_t written = 0;
got = pread(input, buffer, amount, offset);
if (got < 0) {
if (errno == EINTR) continue;
goto done;
}
if (!got) goto done;
offset += got;
while (written < (size_t)got) {
ssize_t sent = write(output, buffer + written, (size_t)got - written);
if (sent < 0 && errno == EINTR) continue;
if (sent <= 0) goto done;
written += (size_t)sent;
}
}
if (fsync(output)) goto done;
ok = 1;
done:
if (output >= 0) close(output);
if (!ok) { if (output >= 0) unlink(path); free(path); path = NULL; }
return path;
}
char *holy_stage_local(const char *source, const char *prefix)
{
int input = open(source, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
char *path;
if (input < 0) return NULL;
path = holy_stage_fd(input, prefix);
close(input);
return path;
}