#include "lib-signals.h"
#include "env-util.h"
#include "fd-set-nonblock.h"
+#include "close-keep-errno.h"
#include "istream.h"
#include "istream-seekable.h"
+#include "safe-mkstemp.h"
#include "str.h"
#include "str-sanitize.h"
#include "strescape.h"
return ret;
}
+static int seekable_fd_callback(const char **path_r, void *context)
+{
+ struct mail_deliver_context *ctx = context;
+ string_t *path;
+ int fd;
+
+ path = t_str_new(128);
+ str_append(path, mail_user_get_temp_prefix(ctx->dest_user));
+ fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
+ if (fd == -1) {
+ i_error("safe_mkstemp(%s) failed: %m", str_c(path));
+ return -1;
+ }
+
+ /* we just want the fd, unlink it */
+ if (unlink(str_c(path)) < 0) {
+ /* shouldn't happen.. */
+ i_error("unlink(%s) failed: %m", str_c(path));
+ close_keep_errno(fd);
+ return -1;
+ }
+
+ *path_r = str_c(path);
+ return fd;
+}
static struct istream *
create_raw_stream(struct mail_deliver_context *ctx,
- const char *temp_path_prefix, int fd, time_t *mtime_r)
+ int fd, time_t *mtime_r)
{
struct istream *input, *input2, *input_list[2];
const unsigned char *data;
input_list[0] = input2; input_list[1] = NULL;
input = i_stream_create_seekable(input_list, MAIL_MAX_MEMORY_BUFFER,
- temp_path_prefix);
+ seekable_fd_callback, ctx);
i_stream_unref(&input2);
return input;
}
if (mail_storage_create(raw_ns, "raw", 0, &errstr) < 0)
i_fatal("Couldn't create internal raw storage: %s", errstr);
if (path == NULL) {
- const char *prefix = mail_user_get_temp_prefix(ctx.dest_user);
- input = create_raw_stream(&ctx, prefix, 0, &mtime);
+ input = create_raw_stream(&ctx, 0, &mtime);
box = mailbox_open(&raw_ns->storage, "Dovecot Delivery Mail",
input, MAILBOX_OPEN_NO_INDEX_FILES);
i_stream_unref(&input);
#include "lib.h"
#include "buffer.h"
#include "close-keep-errno.h"
-#include "hex-binary.h"
-#include "randgen.h"
#include "write-full.h"
#include "istream-internal.h"
#include "istream-concat.h"
#include "istream-seekable.h"
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <time.h>
-
#define BUF_INITIAL_SIZE (1024*32)
struct seekable_istream {
struct istream_private istream;
- char *temp_prefix;
+ char *temp_path;
uoff_t write_peak;
+ int (*fd_callback)(const char **path_r, void *context);
+ void *context;
+
buffer_t *buffer;
struct istream **input, *cur_input;
struct istream *fd_input;
for (i = 0; sstream->input[i] != NULL; i++)
i_stream_unref(&sstream->input[i]);
- i_free(sstream->temp_prefix);
+ i_free(sstream->temp_path);
}
static void
static int copy_to_temp_file(struct seekable_istream *sstream)
{
- unsigned char randbuf[8];
const char *path;
- struct stat st;
int fd;
- /* create a temporary file */
- for (;;) {
- random_fill_weak(randbuf, sizeof(randbuf));
- path = t_strconcat(sstream->temp_prefix,
- dec2str(time(NULL)), ".",
- dec2str(getpid()), ".",
- binary_to_hex(randbuf, sizeof(randbuf)),
- NULL);
- if (stat(path, &st) == 0)
- continue;
-
- if (errno != ENOENT) {
- i_error("stat(%s) failed: %m", path);
- return -1;
- }
-
- fd = open(path, O_RDWR | O_EXCL | O_CREAT, 0600);
- if (fd != -1)
- break;
-
- if (errno != EEXIST) {
- i_error("creat(%s) failed: %m", path);
- return -1;
- }
- }
-
- /* we just want the fd, unlink it */
- if (unlink(path) < 0) {
- /* shouldn't happen.. */
- i_error("unlink(%s) failed: %m", path);
- close_keep_errno(fd);
+ fd = sstream->fd_callback(&path, sstream->context);
+ if (fd == -1)
return -1;
- }
+
+ sstream->temp_path = i_strdup(path);
/* copy our currently read buffer to it */
if (write_full(fd, sstream->buffer->data, sstream->buffer->used) < 0) {
if (write_full(sstream->fd, data, size) < 0) {
i_assert(errno != 0);
stream->istream.stream_errno = errno;
- i_error("write_full(%s...) failed: %m",
- sstream->temp_prefix);
+ i_error("write_full(%s) failed: %m",
+ sstream->temp_path);
i_stream_close(&stream->istream);
return -1;
}
struct istream *
i_stream_create_seekable(struct istream *input[],
- size_t max_buffer_size, const char *temp_prefix)
+ size_t max_buffer_size,
+ int (*fd_callback)(const char **path_r, void *context),
+ void *context)
{
struct seekable_istream *sstream;
const unsigned char *data;
i_assert(count != 0);
sstream = i_new(struct seekable_istream, 1);
- sstream->temp_prefix = i_strdup(temp_prefix);
+ sstream->fd_callback = fd_callback;
+ sstream->context = context;
sstream->buffer = buffer_create_dynamic(default_pool, BUF_INITIAL_SIZE);
sstream->istream.max_buffer_size = max_buffer_size;
/* Create a seekable stream from given NULL-terminated list of input streams.
Try to keep it in memory, but use a temporary file if it's too large.
- temp_prefix is used as path and filename prefix for creating the file.
- It will be appended by PID, timestamp and 128 bits of weak randomness. */
+ When max_buffer_size is reached, fd_callback is called. It should return
+ the fd and path of the created file. Typically the callback would also
+ unlink the file before returning. */
struct istream *
i_stream_create_seekable(struct istream *input[],
- size_t max_buffer_size, const char *temp_prefix);
+ size_t max_buffer_size,
+ int (*fd_callback)(const char **path_r, void *context),
+ void *context);
#endif