view src/lib-fs/fs-sis-queue.c @ 22588:041460202062

ostream-multiplex: Unreference stream parent Otherwise it won't get free'd.
author Aki Tuomi <aki.tuomi@dovecot.fi>
date Mon, 09 Oct 2017 18:21:24 +0300
parents 2e2563132d5f
children cb108f786fb4
line wrap: on
line source

/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "str.h"
#include "istream.h"
#include "ostream.h"
#include "fs-sis-common.h"

#define QUEUE_DIR_NAME "queue"

struct sis_queue_fs {
	struct fs fs;
	char *queue_dir;
};

struct sis_queue_fs_file {
	struct fs_file file;
	struct sis_queue_fs *fs;
};

static struct fs *fs_sis_queue_alloc(void)
{
	struct sis_queue_fs *fs;

	fs = i_new(struct sis_queue_fs, 1);
	fs->fs = fs_class_sis_queue;
	return &fs->fs;
}

static int
fs_sis_queue_init(struct fs *_fs, const char *args,
		  const struct fs_settings *set)
{
	struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs;
	const char *p, *parent_name, *parent_args, *error;

	/* <queue_dir>:<parent fs>[:<args>] */

	p = strchr(args, ':');
	if (p == NULL || p[1] == '\0') {
		fs_set_error(_fs, "Parent filesystem not given as parameter");
		return -1;
	}

	fs->queue_dir = i_strdup_until(args, p);
	parent_name = p + 1;

	parent_args = strchr(parent_name, ':');
	if (parent_args == NULL)
		parent_args = "";
	else
		parent_name = t_strdup_until(parent_name, parent_args++);
	if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) {
		fs_set_error(_fs, "%s", error);
		return -1;
	}
	return 0;
}

static void fs_sis_queue_deinit(struct fs *_fs)
{
	struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs;

	if (_fs->parent != NULL)
		fs_deinit(&_fs->parent);
	i_free(fs->queue_dir);
	i_free(fs);
}

static struct fs_file *
fs_sis_queue_file_init(struct fs *_fs, const char *path,
		       enum fs_open_mode mode, enum fs_open_flags flags)
{
	struct sis_queue_fs *fs = (struct sis_queue_fs *)_fs;
	struct sis_queue_fs_file *file;

	file = i_new(struct sis_queue_fs_file, 1);
	file->file.fs = _fs;
	file->file.path = i_strdup(path);
	file->fs = fs;

	if (mode == FS_OPEN_MODE_APPEND)
		fs_set_error(_fs, "APPEND mode not supported");
	else
		file->file.parent = fs_file_init(_fs->parent, path, mode | flags);
	return &file->file;
}

static void fs_sis_queue_file_deinit(struct fs_file *_file)
{
	struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file;

	if (_file->parent != NULL)
		fs_file_deinit(&_file->parent);
	i_free(file->file.path);
	i_free(file);
}

static void fs_sis_queue_add(struct sis_queue_fs_file *file)
{
	struct sis_queue_fs *fs = (struct sis_queue_fs *)file->file.fs;
	struct fs_file *queue_file;
	const char *fname, *path, *queue_path;

	path = fs_file_path(&file->file);
	fname = strrchr(path, '/');
	if (fname != NULL)
		fname++;
	else
		fname = path;

	queue_path = t_strdup_printf("%s/%s", fs->queue_dir, fname);
	queue_file = fs_file_init(fs->fs.parent, queue_path, FS_OPEN_MODE_CREATE);
	if (fs_write(queue_file, "", 0) < 0 && errno != EEXIST)
		i_error("fs-sis-queue: %s", fs_file_last_error(queue_file));
	fs_file_deinit(&queue_file);
}

static int fs_sis_queue_write(struct fs_file *_file, const void *data, size_t size)
{
	struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file;

	if (_file->parent == NULL)
		return -1;
	if (fs_write(_file->parent, data, size) < 0)
		return -1;
	T_BEGIN {
		fs_sis_queue_add(file);
	} T_END;
	return 0;
}

static void fs_sis_queue_write_stream(struct fs_file *_file)
{
	i_assert(_file->output == NULL);

	if (_file->parent == NULL) {
		_file->output = o_stream_create_error_str(EINVAL, "%s",
						fs_file_last_error(_file));
	} else {
		_file->output = fs_write_stream(_file->parent);
	}
	o_stream_set_name(_file->output, _file->path);
}

static int fs_sis_queue_write_stream_finish(struct fs_file *_file, bool success)
{
	struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file;

	if (!success) {
		if (_file->parent != NULL)
			fs_write_stream_abort_parent(_file, &_file->output);
		return -1;
	}

	if (fs_write_stream_finish(_file->parent, &_file->output) < 0)
		return -1;
	T_BEGIN {
		fs_sis_queue_add(file);
	} T_END;
	return 1;
}

static int fs_sis_queue_delete(struct fs_file *_file)
{
	T_BEGIN {
		fs_sis_try_unlink_hash_file(_file->fs, _file->parent);
	} T_END;
	return fs_delete(_file->parent);
}

const struct fs fs_class_sis_queue = {
	.name = "sis-queue",
	.v = {
		fs_sis_queue_alloc,
		fs_sis_queue_init,
		fs_sis_queue_deinit,
		fs_wrapper_get_properties,
		fs_sis_queue_file_init,
		fs_sis_queue_file_deinit,
		fs_wrapper_file_close,
		fs_wrapper_file_get_path,
		fs_wrapper_set_async_callback,
		fs_wrapper_wait_async,
		fs_wrapper_set_metadata,
		fs_wrapper_get_metadata,
		fs_wrapper_prefetch,
		fs_wrapper_read,
		fs_wrapper_read_stream,
		fs_sis_queue_write,
		fs_sis_queue_write_stream,
		fs_sis_queue_write_stream_finish,
		fs_wrapper_lock,
		fs_wrapper_unlock,
		fs_wrapper_exists,
		fs_wrapper_stat,
		fs_wrapper_copy,
		fs_wrapper_rename,
		fs_sis_queue_delete,
		fs_wrapper_iter_init,
		NULL,
		NULL,
		NULL,
		fs_wrapper_get_nlinks,
	}
};