diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/plugins/fs-compress/fs-compress.c | |
parent | Initial commit. (diff) | |
download | dovecot-upstream.tar.xz dovecot-upstream.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/plugins/fs-compress/fs-compress.c')
-rw-r--r-- | src/plugins/fs-compress/fs-compress.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/plugins/fs-compress/fs-compress.c b/src/plugins/fs-compress/fs-compress.c new file mode 100644 index 0000000..ab03076 --- /dev/null +++ b/src/plugins/fs-compress/fs-compress.c @@ -0,0 +1,285 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "istream.h" +#include "istream-tee.h" +#include "istream-try.h" +#include "ostream.h" +#include "iostream-temp.h" +#include "compression.h" +#include "fs-api-private.h" + +struct compress_fs { + struct fs fs; + const struct compression_handler *compress_handler; + int compress_level; + bool try_plain; +}; + +struct compress_fs_file { + struct fs_file file; + struct compress_fs *fs; + struct fs_file *super_read; + enum fs_open_mode open_mode; + struct istream *input; + + struct ostream *super_output; + struct ostream *temp_output; +}; + +#define COMPRESS_FS(ptr) container_of((ptr), struct compress_fs, fs) +#define COMPRESS_FILE(ptr) container_of((ptr), struct compress_fs_file, file) + +extern const struct fs fs_class_compress; + +static struct fs *fs_compress_alloc(void) +{ + struct compress_fs *fs; + + fs = i_new(struct compress_fs, 1); + fs->fs = fs_class_compress; + return &fs->fs; +} + +static int +fs_compress_init(struct fs *_fs, const char *args, + const struct fs_settings *set, const char **error_r) +{ + struct compress_fs *fs = COMPRESS_FS(_fs); + const char *p, *compression_name, *level_str; + const char *parent_name, *parent_args; + int ret; + + /* get compression handler name */ + if (str_begins(args, "maybe-")) { + fs->try_plain = TRUE; + args += 6; + } + + p = strchr(args, ':'); + if (p == NULL) { + *error_r = "Compression method not given as parameter"; + return -1; + } + compression_name = t_strdup_until(args, p++); + args = p; + + /* get compression level */ + p = strchr(args, ':'); + if (p == NULL || p[1] == '\0') { + *error_r = "Parent filesystem not given as parameter"; + return -1; + } + + level_str = t_strdup_until(args, p++); + args = p; + ret = compression_lookup_handler(compression_name, &fs->compress_handler); + if (ret <= 0) { + *error_r = t_strdup_printf("Compression method '%s' %s.", + compression_name, ret == 0 ? + "not supported" : "unknown"); + return -1; + } + if (str_to_int(level_str, &fs->compress_level) < 0 || + fs->compress_level < fs->compress_handler->get_min_level() || + fs->compress_level > fs->compress_handler->get_max_level()) { + *error_r = t_strdup_printf( + "Invalid compression level parameter '%s': " + "Level must be between %d..%d", level_str, + fs->compress_handler->get_min_level(), + fs->compress_handler->get_max_level()); + return -1; + } + parent_args = strchr(args, ':'); + if (parent_args == NULL) { + parent_name = args; + parent_args = ""; + } else { + parent_name = t_strdup_until(args, parent_args); + parent_args++; + } + return fs_init(parent_name, parent_args, set, &_fs->parent, error_r); +} + +static void fs_compress_free(struct fs *_fs) +{ + struct compress_fs *fs = COMPRESS_FS(_fs); + + i_free(fs); +} + +static struct fs_file *fs_compress_file_alloc(void) +{ + struct compress_fs_file *file = i_new(struct compress_fs_file, 1); + return &file->file; +} + +static void +fs_compress_file_init(struct fs_file *_file, const char *path, + enum fs_open_mode mode, enum fs_open_flags flags) +{ + struct compress_fs *fs = COMPRESS_FS(_file->fs); + struct compress_fs_file *file = COMPRESS_FILE(_file); + + file->file.path = i_strdup(path); + file->fs = fs; + file->open_mode = mode; + + /* avoid unnecessarily creating two seekable streams */ + flags &= ENUM_NEGATE(FS_OPEN_FLAG_SEEKABLE); + + file->file.parent = fs_file_init_parent(_file, path, mode, flags); + if (mode == FS_OPEN_MODE_READONLY && + (flags & FS_OPEN_FLAG_ASYNC) == 0) { + /* use async stream for parent, so fs_read_stream() won't create + another seekable stream needlessly */ + file->super_read = fs_file_init_parent(_file, path, + mode, flags | FS_OPEN_FLAG_ASYNC | + FS_OPEN_FLAG_ASYNC_NOQUEUE); + } else { + file->super_read = file->file.parent; + } +} + +static void fs_compress_file_deinit(struct fs_file *_file) +{ + struct compress_fs_file *file = COMPRESS_FILE(_file); + + if (file->super_read != _file->parent) + fs_file_deinit(&file->super_read); + fs_file_free(_file); + i_free(file->file.path); + i_free(file); +} + +static void fs_compress_file_close(struct fs_file *_file) +{ + struct compress_fs_file *file = COMPRESS_FILE(_file); + + i_stream_unref(&file->input); + fs_file_close(file->super_read); + fs_file_close(_file->parent); +} + +static struct istream * +fs_compress_read_stream(struct fs_file *_file, size_t max_buffer_size) +{ + struct compress_fs_file *file = COMPRESS_FILE(_file); + struct istream *input; + enum istream_decompress_flags flags = 0; + + if (file->input != NULL) { + i_stream_ref(file->input); + i_stream_seek(file->input, 0); + return file->input; + } + + input = fs_read_stream(file->super_read, max_buffer_size); + if (file->fs->try_plain) + flags |= ISTREAM_DECOMPRESS_FLAG_TRY; + file->input = i_stream_create_decompress(input, flags); + i_stream_unref(&input); + i_stream_ref(file->input); + return file->input; +} + +static void fs_compress_write_stream(struct fs_file *_file) +{ + struct compress_fs_file *file = COMPRESS_FILE(_file); + + if (file->fs->compress_level == 0) { + fs_wrapper_write_stream(_file); + return; + } + + i_assert(_file->output == NULL); + + file->temp_output = + iostream_temp_create_named(_file->fs->temp_path_prefix, + IOSTREAM_TEMP_FLAG_TRY_FD_DUP, + fs_file_path(_file)); + _file->output = file->fs->compress_handler-> + create_ostream(file->temp_output, file->fs->compress_level); +} + +static int fs_compress_write_stream_finish(struct fs_file *_file, bool success) +{ + struct compress_fs_file *file = COMPRESS_FILE(_file); + struct istream *input; + int ret; + + if (file->fs->compress_level == 0) + return fs_wrapper_write_stream_finish(_file, success); + + if (_file->output != NULL) { + if (_file->output->closed) + success = FALSE; + if (_file->output == file->super_output) + _file->output = NULL; + else + o_stream_unref(&_file->output); + } + if (!success) { + o_stream_destroy(&file->temp_output); + if (file->super_output != NULL) + fs_write_stream_abort_parent(_file, &file->super_output); + return -1; + } + + if (file->super_output != NULL) { + i_assert(file->temp_output == NULL); + return fs_write_stream_finish(_file->parent, &file->super_output); + } + if (file->temp_output == NULL) { + /* finishing up */ + i_assert(file->super_output == NULL); + return fs_write_stream_finish(_file->parent, &file->temp_output); + } + /* finish writing the temporary file */ + input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE); + file->super_output = fs_write_stream(_file->parent); + o_stream_nsend_istream(file->super_output, input); + ret = fs_write_stream_finish(_file->parent, &file->super_output); + i_stream_unref(&input); + return ret; +} + +const struct fs fs_class_compress = { + .name = "compress", + .v = { + fs_compress_alloc, + fs_compress_init, + NULL, + fs_compress_free, + fs_wrapper_get_properties, + fs_compress_file_alloc, + fs_compress_file_init, + fs_compress_file_deinit, + fs_compress_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_read_via_stream, + fs_compress_read_stream, + fs_write_via_stream, + fs_compress_write_stream, + fs_compress_write_stream_finish, + fs_wrapper_lock, + fs_wrapper_unlock, + fs_wrapper_exists, + fs_wrapper_stat, + fs_wrapper_copy, + fs_wrapper_rename, + fs_wrapper_delete, + fs_wrapper_iter_alloc, + fs_wrapper_iter_init, + fs_wrapper_iter_next, + fs_wrapper_iter_deinit, + NULL, + fs_wrapper_get_nlinks + } +}; |