diff options
author | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:01:35 +0200 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:01:35 +0200 |
commit | 6fb3965e81d9e4278ee5d96c2d68c014df1f3802 (patch) | |
tree | c3c895a4a39d4888e0ff978c97587120712a6b9d /smtpd/queue_fs.c | |
parent | plug ubuntu-gcc10 to CI (diff) | |
download | OpenSMTPD-6fb3965e81d9e4278ee5d96c2d68c014df1f3802.tar.xz OpenSMTPD-6fb3965e81d9e4278ee5d96c2d68c014df1f3802.zip |
move
Diffstat (limited to 'smtpd/queue_fs.c')
-rw-r--r-- | smtpd/queue_fs.c | 695 |
1 files changed, 0 insertions, 695 deletions
diff --git a/smtpd/queue_fs.c b/smtpd/queue_fs.c deleted file mode 100644 index 097ba1e2..00000000 --- a/smtpd/queue_fs.c +++ /dev/null @@ -1,695 +0,0 @@ -/* $OpenBSD: queue_fs.c,v 1.20 2020/02/25 17:03:13 millert Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#if HAVE_SYS_MOUNT_H -#include <sys/mount.h> -#endif -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> -#ifdef HAVE_SYS_STATFS_H -#include <sys/statfs.h> -#endif - -#include <ctype.h> -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <fts.h> -#include <imsg.h> -#include <inttypes.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" - -#define PATH_QUEUE "/queue" -#define PATH_INCOMING "/incoming" -#define PATH_EVPTMP PATH_INCOMING "/envelope.tmp" -#define PATH_MESSAGE "/message" - -/* percentage of remaining space / inodes required to accept new messages */ -#define MINSPACE 5 -#define MININODES 5 - -struct qwalk { - FTS *fts; - int depth; -}; - -static int fsqueue_check_space(void); -static void fsqueue_envelope_path(uint64_t, char *, size_t); -static void fsqueue_envelope_incoming_path(uint64_t, char *, size_t); -static int fsqueue_envelope_dump(char *, const char *, size_t, int, int); -static void fsqueue_message_path(uint32_t, char *, size_t); -static void fsqueue_message_incoming_path(uint32_t, char *, size_t); -static void *fsqueue_qwalk_new(void); -static int fsqueue_qwalk(void *, uint64_t *); -static void fsqueue_qwalk_close(void *); - -struct tree evpcount; -static struct timespec startup; - -#define REF (int*)0xf00 - -static int -queue_fs_message_create(uint32_t *msgid) -{ - char rootdir[PATH_MAX]; - struct stat sb; - - if (!fsqueue_check_space()) - return 0; - -again: - *msgid = queue_generate_msgid(); - - /* prevent possible collision later when moving to Q_QUEUE */ - fsqueue_message_path(*msgid, rootdir, sizeof(rootdir)); - if (stat(rootdir, &sb) != -1) - goto again; - - /* we hit an unexpected error, temporarily fail */ - if (errno != ENOENT) { - *msgid = 0; - return 0; - } - - fsqueue_message_incoming_path(*msgid, rootdir, sizeof(rootdir)); - if (mkdir(rootdir, 0700) == -1) { - if (errno == EEXIST) - goto again; - - if (errno == ENOSPC) { - *msgid = 0; - return 0; - } - - log_warn("warn: queue-fs: mkdir"); - *msgid = 0; - return 0; - } - - return (1); -} - -static int -queue_fs_message_commit(uint32_t msgid, const char *path) -{ - char incomingdir[PATH_MAX]; - char queuedir[PATH_MAX]; - char msgdir[PATH_MAX]; - char msgpath[PATH_MAX]; - - /* before-first, move the message content in the incoming directory */ - fsqueue_message_incoming_path(msgid, msgpath, sizeof(msgpath)); - if (strlcat(msgpath, PATH_MESSAGE, sizeof(msgpath)) - >= sizeof(msgpath)) - return (0); - if (rename(path, msgpath) == -1) - return (0); - - fsqueue_message_incoming_path(msgid, incomingdir, sizeof(incomingdir)); - fsqueue_message_path(msgid, msgdir, sizeof(msgdir)); - if (strlcpy(queuedir, msgdir, sizeof(queuedir)) - >= sizeof(queuedir)) - return (0); - - /* first attempt to rename */ - if (rename(incomingdir, msgdir) == 0) - return 1; - if (errno == ENOSPC) - return 0; - if (errno != ENOENT) { - log_warn("warn: queue-fs: rename"); - return 0; - } - - /* create the bucket */ - *strrchr(queuedir, '/') = '\0'; - if (mkdir(queuedir, 0700) == -1) { - if (errno == ENOSPC) - return 0; - if (errno != EEXIST) { - log_warn("warn: queue-fs: mkdir"); - return 0; - } - } - - /* rename */ - if (rename(incomingdir, msgdir) == -1) { - if (errno == ENOSPC) - return 0; - log_warn("warn: queue-fs: rename"); - return 0; - } - - return 1; -} - -static int -queue_fs_message_fd_r(uint32_t msgid) -{ - int fd; - char path[PATH_MAX]; - - fsqueue_message_path(msgid, path, sizeof(path)); - if (strlcat(path, PATH_MESSAGE, sizeof(path)) - >= sizeof(path)) - return -1; - - if ((fd = open(path, O_RDONLY)) == -1) { - log_warn("warn: queue-fs: open"); - return -1; - } - - return fd; -} - -static int -queue_fs_message_delete(uint32_t msgid) -{ - char path[PATH_MAX]; - struct stat sb; - - fsqueue_message_incoming_path(msgid, path, sizeof(path)); - if (stat(path, &sb) == -1) - fsqueue_message_path(msgid, path, sizeof(path)); - - if (rmtree(path, 0) == -1) - log_warn("warn: queue-fs: rmtree"); - - tree_pop(&evpcount, msgid); - - return 1; -} - -static int -queue_fs_envelope_create(uint32_t msgid, const char *buf, size_t len, - uint64_t *evpid) -{ - char path[PATH_MAX]; - int queued = 0, i, r = 0, *n; - struct stat sb; - - if (msgid == 0) { - log_warnx("warn: queue-fs: msgid=0, evpid=%016"PRIx64, *evpid); - goto done; - } - - fsqueue_message_incoming_path(msgid, path, sizeof(path)); - if (stat(path, &sb) == -1) - queued = 1; - - for (i = 0; i < 20; i ++) { - *evpid = queue_generate_evpid(msgid); - if (queued) - fsqueue_envelope_path(*evpid, path, sizeof(path)); - else - fsqueue_envelope_incoming_path(*evpid, path, - sizeof(path)); - - if ((r = fsqueue_envelope_dump(path, buf, len, 0, 0)) != 0) - goto done; - } - r = 0; - log_warnx("warn: queue-fs: could not allocate evpid"); - -done: - if (r) { - n = tree_pop(&evpcount, msgid); - if (n == NULL) - n = REF; - n += 1; - tree_xset(&evpcount, msgid, n); - } - return (r); -} - -static int -queue_fs_envelope_load(uint64_t evpid, char *buf, size_t len) -{ - char pathname[PATH_MAX]; - FILE *fp; - size_t r; - - fsqueue_envelope_path(evpid, pathname, sizeof(pathname)); - - fp = fopen(pathname, "r"); - if (fp == NULL) { - if (errno != ENOENT && errno != ENFILE) - log_warn("warn: queue-fs: fopen"); - return 0; - } - - r = fread(buf, 1, len, fp); - if (r) { - if (r == len) { - log_warn("warn: queue-fs: too large"); - r = 0; - } - else - buf[r] = '\0'; - } - fclose(fp); - - return (r); -} - -static int -queue_fs_envelope_update(uint64_t evpid, const char *buf, size_t len) -{ - char dest[PATH_MAX]; - - fsqueue_envelope_path(evpid, dest, sizeof(dest)); - - return (fsqueue_envelope_dump(dest, buf, len, 1, 1)); -} - -static int -queue_fs_envelope_delete(uint64_t evpid) -{ - char pathname[PATH_MAX]; - uint32_t msgid; - int *n; - - fsqueue_envelope_path(evpid, pathname, sizeof(pathname)); - if (unlink(pathname) == -1) - if (errno != ENOENT) - return 0; - - msgid = evpid_to_msgid(evpid); - n = tree_pop(&evpcount, msgid); - n -= 1; - - if (n - REF == 0) - queue_fs_message_delete(msgid); - else - tree_xset(&evpcount, msgid, n); - - return (1); -} - -static int -queue_fs_message_walk(uint64_t *evpid, char *buf, size_t len, - uint32_t msgid, int *done, void **data) -{ - struct dirent *dp; - DIR *dir = *data; - char path[PATH_MAX]; - char msgid_str[9]; - char *tmp; - int r, *n; - - if (*done) - return (-1); - - if (!bsnprintf(path, sizeof path, "%s/%02x/%08x", - PATH_QUEUE, (msgid & 0xff000000) >> 24, msgid)) - fatalx("queue_fs_message_walk: path does not fit buffer"); - - if (dir == NULL) { - if ((dir = opendir(path)) == NULL) { - log_warn("warn: queue_fs: opendir: %s", path); - *done = 1; - return (-1); - } - - *data = dir; - } - - (void)snprintf(msgid_str, sizeof msgid_str, "%08" PRIx32, msgid); - while ((dp = readdir(dir)) != NULL) { -#if defined(HAVE_STRUCT_DIR_D_TYPE) - if (dp->d_type != DT_REG) - continue; -#endif - - /* ignore files other than envelopes */ - if (strlen(dp->d_name) != 16 || - strncmp(dp->d_name, msgid_str, 8)) - continue; - - tmp = NULL; - *evpid = strtoull(dp->d_name, &tmp, 16); - if (tmp && *tmp != '\0') { - log_debug("debug: fsqueue: bogus file %s", dp->d_name); - continue; - } - - memset(buf, 0, len); - r = queue_fs_envelope_load(*evpid, buf, len); - if (r) { - n = tree_pop(&evpcount, msgid); - if (n == NULL) - n = REF; - - n += 1; - tree_xset(&evpcount, msgid, n); - } - - return (r); - } - - (void)closedir(dir); - *done = 1; - return (-1); -} - -static int -queue_fs_envelope_walk(uint64_t *evpid, char *buf, size_t len) -{ - static int done = 0; - static void *hdl = NULL; - int r, *n; - uint32_t msgid; - - if (done) - return (-1); - - if (hdl == NULL) - hdl = fsqueue_qwalk_new(); - - if (fsqueue_qwalk(hdl, evpid)) { - memset(buf, 0, len); - r = queue_fs_envelope_load(*evpid, buf, len); - if (r) { - msgid = evpid_to_msgid(*evpid); - n = tree_pop(&evpcount, msgid); - if (n == NULL) - n = REF; - n += 1; - tree_xset(&evpcount, msgid, n); - } - return (r); - } - - fsqueue_qwalk_close(hdl); - done = 1; - return (-1); -} - -static int -fsqueue_check_space(void) -{ -#ifdef __OpenBSD__ - struct statfs buf; - uint64_t used; - uint64_t total; - - if (statfs(PATH_QUEUE, &buf) == -1) { - log_warn("warn: queue-fs: statfs"); - return 0; - } - - /* - * f_bfree and f_ffree is not set on all filesystems. - * They could be signed or unsigned integers. - * Some systems will set them to 0, others will set them to -1. - */ - if (buf.f_bfree == 0 || buf.f_ffree == 0 || - (int64_t)buf.f_bfree == -1 || (int64_t)buf.f_ffree == -1) - return 1; - - used = buf.f_blocks - buf.f_bfree; - total = buf.f_bavail + used; - if (total != 0) - used = (float)used / (float)total * 100; - else - used = 100; - if (100 - used < MINSPACE) { - log_warnx("warn: not enough disk space: %llu%% left", - (unsigned long long) 100 - used); - log_warnx("warn: temporarily rejecting messages"); - return 0; - } - - used = buf.f_files - buf.f_ffree; - total = buf.f_favail + used; - if (total != 0) - used = (float)used / (float)total * 100; - else - used = 100; - if (100 - used < MININODES) { - log_warnx("warn: not enough inodes: %llu%% left", - (unsigned long long) 100 - used); - log_warnx("warn: temporarily rejecting messages"); - return 0; - } -#endif - return 1; -} - -static void -fsqueue_envelope_path(uint64_t evpid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%02x/%08x/%016" PRIx64, - PATH_QUEUE, - (evpid_to_msgid(evpid) & 0xff000000) >> 24, - evpid_to_msgid(evpid), - evpid)) - fatalx("fsqueue_envelope_path: path does not fit buffer"); -} - -static void -fsqueue_envelope_incoming_path(uint64_t evpid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%08x/%016" PRIx64, - PATH_INCOMING, - evpid_to_msgid(evpid), - evpid)) - fatalx("fsqueue_envelope_incoming_path: path does not fit buffer"); -} - -static int -fsqueue_envelope_dump(char *dest, const char *evpbuf, size_t evplen, - int do_atomic, int do_sync) -{ - const char *path = do_atomic ? PATH_EVPTMP : dest; - FILE *fp = NULL; - int fd; - size_t w; - - if ((fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) { - log_warn("warn: queue-fs: open"); - goto tempfail; - } - - if ((fp = fdopen(fd, "w")) == NULL) { - log_warn("warn: queue-fs: fdopen"); - goto tempfail; - } - - w = fwrite(evpbuf, 1, evplen, fp); - if (w < evplen) { - log_warn("warn: queue-fs: short write"); - goto tempfail; - } - if (fflush(fp)) { - log_warn("warn: queue-fs: fflush"); - goto tempfail; - } - if (do_sync && fsync(fileno(fp))) { - log_warn("warn: queue-fs: fsync"); - goto tempfail; - } - if (fclose(fp) != 0) { - log_warn("warn: queue-fs: fclose"); - fp = NULL; - goto tempfail; - } - fp = NULL; - fd = -1; - - if (do_atomic && rename(path, dest) == -1) { - log_warn("warn: queue-fs: rename"); - goto tempfail; - } - return (1); - -tempfail: - if (fp) - fclose(fp); - else if (fd != -1) - close(fd); - if (unlink(path) == -1) - log_warn("warn: queue-fs: unlink"); - return (0); -} - -static void -fsqueue_message_path(uint32_t msgid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%02x/%08x", - PATH_QUEUE, - (msgid & 0xff000000) >> 24, - msgid)) - fatalx("fsqueue_message_path: path does not fit buffer"); -} - -static void -fsqueue_message_incoming_path(uint32_t msgid, char *buf, size_t len) -{ - if (!bsnprintf(buf, len, "%s/%08x", - PATH_INCOMING, - msgid)) - fatalx("fsqueue_message_incoming_path: path does not fit buffer"); -} - -static void * -fsqueue_qwalk_new(void) -{ - char path[PATH_MAX]; - char * const path_argv[] = { path, NULL }; - struct qwalk *q; - - q = xcalloc(1, sizeof(*q)); - (void)strlcpy(path, PATH_QUEUE, sizeof(path)); - q->fts = fts_open(path_argv, - FTS_PHYSICAL | FTS_NOCHDIR, NULL); - - if (q->fts == NULL) - err(1, "fsqueue_qwalk_new: fts_open: %s", path); - - return (q); -} - -static void -fsqueue_qwalk_close(void *hdl) -{ - struct qwalk *q = hdl; - - fts_close(q->fts); - - free(q); -} - -static int -fsqueue_qwalk(void *hdl, uint64_t *evpid) -{ - struct qwalk *q = hdl; - FTSENT *e; - char *tmp; - - while ((e = fts_read(q->fts)) != NULL) { - switch (e->fts_info) { - case FTS_D: - q->depth += 1; - if (q->depth == 2 && e->fts_namelen != 2) { - log_debug("debug: fsqueue: bogus directory %s", - e->fts_path); - fts_set(q->fts, e, FTS_SKIP); - break; - } - if (q->depth == 3 && e->fts_namelen != 8) { - log_debug("debug: fsqueue: bogus directory %s", - e->fts_path); - fts_set(q->fts, e, FTS_SKIP); - break; - } - break; - - case FTS_DP: - case FTS_DNR: - q->depth -= 1; - break; - - case FTS_F: - if (q->depth != 3) - break; - if (e->fts_namelen != 16) - break; -#if HAVE_STRUCT_STAT_ST_MTIM - if (timespeccmp(&e->fts_statp->st_mtim, &startup, >)) -#endif -#if HAVE_STRUCT_STAT_ST_MTIMSPEC - if (timespeccmp(&e->fts_statp->st_mtimspec, &startup, >)) -#endif - break; - tmp = NULL; - *evpid = strtoull(e->fts_name, &tmp, 16); - if (tmp && *tmp != '\0') { - log_debug("debug: fsqueue: bogus file %s", - e->fts_path); - break; - } - return (1); - default: - break; - } - } - - return (0); -} - -static int -queue_fs_init(struct passwd *pw, int server, const char *conf) -{ - unsigned int n; - char *paths[] = { PATH_QUEUE, PATH_INCOMING }; - char path[PATH_MAX]; - int ret; - - /* remove incoming/ if it exists */ - if (server) - mvpurge(PATH_SPOOL PATH_INCOMING, PATH_SPOOL PATH_PURGE); - - fsqueue_envelope_path(0, path, sizeof(path)); - - ret = 1; - for (n = 0; n < nitems(paths); n++) { - (void)strlcpy(path, PATH_SPOOL, sizeof(path)); - if (strlcat(path, paths[n], sizeof(path)) >= sizeof(path)) - errx(1, "path too long %s%s", PATH_SPOOL, paths[n]); - if (ckdir(path, 0700, pw->pw_uid, 0, server) == 0) - ret = 0; - } - - if (clock_gettime(CLOCK_REALTIME, &startup)) - err(1, "clock_gettime"); - - tree_init(&evpcount); - - queue_api_on_message_create(queue_fs_message_create); - queue_api_on_message_commit(queue_fs_message_commit); - queue_api_on_message_delete(queue_fs_message_delete); - queue_api_on_message_fd_r(queue_fs_message_fd_r); - queue_api_on_envelope_create(queue_fs_envelope_create); - queue_api_on_envelope_delete(queue_fs_envelope_delete); - queue_api_on_envelope_update(queue_fs_envelope_update); - queue_api_on_envelope_load(queue_fs_envelope_load); - queue_api_on_envelope_walk(queue_fs_envelope_walk); - queue_api_on_message_walk(queue_fs_message_walk); - - return (ret); -} - -struct queue_backend queue_backend_fs = { - queue_fs_init, -}; |