#define _POSIX_C_SOURCE 200809L #include "stage.h" #include #include #include #include #include #include #include #include #include #include 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; }