doveadm-fs.c revision f98a0796910657153726088fb936737cc585cd63
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2015 Dovecot authors, see the included COPYING file */
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen#include "lib.h"
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen#include "array.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "md5.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "sha2.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "hash-method.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "hex-binary.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "istream.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "ostream.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "iostream-ssl.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "fs-api.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include "doveadm.h"
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include <stdio.h>
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek#include <unistd.h>
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipekstatic void fs_cmd_help(doveadm_command_t *cmd);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipekstatic void cmd_fs_delete(int argc, char *argv[]);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipekstatic struct fs *
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipekcmd_fs_init(int *argc, char **argv[], int own_arg_count, doveadm_command_t *cmd)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek{
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct ssl_iostream_settings ssl_set;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs_settings fs_set;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs *fs;
b9e830a81455faf3c0dadfc9dbf0c7dc8aca955cJosef 'Jeff' Sipek const char *error;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (*argc != 3 + own_arg_count)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_cmd_help(cmd);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek memset(&ssl_set, 0, sizeof(ssl_set));
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek ssl_set.ca_dir = doveadm_settings->ssl_client_ca_dir;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek ssl_set.ca_file = doveadm_settings->ssl_client_ca_file;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek ssl_set.verbose = doveadm_debug;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek memset(&fs_set, 0, sizeof(fs_set));
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_set.ssl_client_set = &ssl_set;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_set.temp_dir = "/tmp";
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_set.base_dir = doveadm_settings->base_dir;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_set.debug = doveadm_debug;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (fs_init((*argv)[1], (*argv)[2], &fs_set, &fs, &error) < 0)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_fatal("fs_init() failed: %s", error);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek *argc += 3;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek *argv += 3;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek return fs;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek}
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipekstatic void cmd_fs_get(int argc, char *argv[])
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek{
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs *fs;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs_file *file;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct istream *input;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek const unsigned char *data;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek size_t size;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek ssize_t ret;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_get);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek file = fs_file_init(fs, argv[0], FS_OPEN_MODE_READONLY);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek input = fs_read_stream(file, IO_BLOCK_SIZE);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fwrite(data, 1, size, stdout);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_stream_skip(input, size);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_assert(ret == -1);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (input->stream_errno == ENOENT) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_error("%s doesn't exist", fs_file_path(file));
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek doveadm_exit_code = DOVEADM_EX_NOTFOUND;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek } else if (input->stream_errno != 0) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_error("read(%s) failed: %m", fs_file_path(file));
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek doveadm_exit_code = EX_TEMPFAIL;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_stream_unref(&input);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_file_deinit(&file);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_deinit(&fs);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek}
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipekstatic void cmd_fs_put(int argc, char *argv[])
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek{
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs *fs;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek enum fs_properties props;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek const char *src_path, *dest_path;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs_file *file;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct istream *input;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct ostream *output;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek buffer_t *hash = NULL;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek off_t ret;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek int c;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek while ((c = getopt(argc, argv, "h:")) > 0) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek switch (c) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek case 'h':
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek hash = buffer_create_dynamic(pool_datastack_create(), 32);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (hex_to_binary(optarg, hash) < 0)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_fatal("Invalid -h parameter: Hash not in hex");
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek break;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek default:
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_cmd_help(cmd_fs_put);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek argc -= optind-1; argv += optind-1;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs = cmd_fs_init(&argc, &argv, 2, cmd_fs_put);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek src_path = argv[0];
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek dest_path = argv[1];
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek file = fs_file_init(fs, dest_path, FS_OPEN_MODE_REPLACE);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek props = fs_get_properties(fs);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (hash == NULL)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek ;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek else if (hash->used == hash_method_md5.digest_size) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if ((props & FS_PROPERTY_WRITE_HASH_MD5) == 0)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_fatal("fs backend doesn't support MD5 hashes");
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_write_set_hash(file,
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek hash_method_lookup(hash_method_md5.name), hash->data);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek } else if (hash->used == hash_method_sha256.digest_size) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if ((props & FS_PROPERTY_WRITE_HASH_SHA256) == 0)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_fatal("fs backend doesn't support SHA256 hashes");
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_write_set_hash(file,
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek hash_method_lookup(hash_method_sha256.name), hash->data);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek output = fs_write_stream(file);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek input = i_stream_create_file(src_path, IO_BLOCK_SIZE);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if ((ret = o_stream_send_istream(output, input)) < 0) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (output->stream_errno != 0)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_error("write(%s) failed: %m", dest_path);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek else
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_error("read(%s) failed: %m", src_path);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek doveadm_exit_code = EX_TEMPFAIL;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_stream_destroy(&input);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (fs_write_stream_finish(file, &output) < 0) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_error("fs_write_stream_finish() failed: %s",
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_file_last_error(file));
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek doveadm_exit_code = EX_TEMPFAIL;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_file_deinit(&file);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_deinit(&fs);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek}
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipekstatic void cmd_fs_copy(int argc, char *argv[])
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek{
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs *fs;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek struct fs_file *src_file, *dest_file;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek const char *src_path, *dest_path;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs = cmd_fs_init(&argc, &argv, 2, cmd_fs_copy);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek src_path = argv[0];
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek dest_path = argv[1];
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek src_file = fs_file_init(fs, src_path, FS_OPEN_MODE_READONLY);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek dest_file = fs_file_init(fs, dest_path, FS_OPEN_MODE_REPLACE);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek if (fs_copy(src_file, dest_file) == 0) ;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek else if (errno == ENOENT) {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_error("%s doesn't exist", src_path);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek doveadm_exit_code = DOVEADM_EX_NOTFOUND;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek } else {
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek i_error("fs_copy(%s, %s) failed: %s",
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek src_path, dest_path, fs_last_error(fs));
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek doveadm_exit_code = EX_TEMPFAIL;
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek }
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_file_deinit(&src_file);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_file_deinit(&dest_file);
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek fs_deinit(&fs);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen}
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainenstatic void cmd_fs_stat(int argc, char *argv[])
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen{
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct fs *fs;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct fs_file *file;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct stat st;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
33750ba29b605a925af5aed58d3f3735422b1e25Phil Carmody fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_stat);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen file = fs_file_init(fs, argv[0], FS_OPEN_MODE_READONLY);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen if (fs_stat(file, &st) == 0) {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen printf("%s size=%lld\n", fs_file_path(file),
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen (long long)st.st_size);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen } else if (errno == ENOENT) {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen i_error("%s doesn't exist", fs_file_path(file));
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen doveadm_exit_code = DOVEADM_EX_NOTFOUND;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen } else {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen i_error("fs_stat(%s) failed: %s",
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen fs_file_path(file), fs_file_last_error(file));
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen doveadm_exit_code = EX_TEMPFAIL;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen }
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen fs_file_deinit(&file);
33750ba29b605a925af5aed58d3f3735422b1e25Phil Carmody fs_deinit(&fs);
33750ba29b605a925af5aed58d3f3735422b1e25Phil Carmody}
33750ba29b605a925af5aed58d3f3735422b1e25Phil Carmody
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainenstatic void cmd_fs_metadata(int argc, char *argv[])
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen{
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct fs *fs;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct fs_file *file;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen const struct fs_metadata *m;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen const ARRAY_TYPE(fs_metadata) *metadata;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_metadata);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen file = fs_file_init(fs, argv[0], FS_OPEN_MODE_READONLY);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen if (fs_get_metadata(file, &metadata) == 0) {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen array_foreach(metadata, m)
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen printf("%s=%s\n", m->key, m->value);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen } else if (errno == ENOENT) {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen i_error("%s doesn't exist", fs_file_path(file));
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen doveadm_exit_code = DOVEADM_EX_NOTFOUND;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen } else {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen i_error("fs_stat(%s) failed: %s",
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen fs_file_path(file), fs_file_last_error(file));
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen doveadm_exit_code = EX_TEMPFAIL;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen }
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen fs_file_deinit(&file);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen fs_deinit(&fs);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen}
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainenstruct fs_delete_file {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct fs_file *file;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen bool finished;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen};
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainenstruct fs_delete_ctx {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen unsigned int files_count;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct fs_delete_file *files;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen};
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainenstatic bool cmd_fs_delete_ctx_run(struct fs_delete_ctx *ctx)
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen{
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen unsigned int i;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen bool ret = FALSE;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen for (i = 0; i < ctx->files_count; i++) {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen if (ctx->files[i].file == NULL || ctx->files[i].finished)
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen ;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen else if (fs_delete(ctx->files[i].file) == 0) {
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen fs_file_deinit(&ctx->files[i].file);
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen ctx->files[i].finished = TRUE;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen } else if (errno == EAGAIN)
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen ret = TRUE;
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen else {
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi i_error("fs_delete(%s) failed: %s",
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi fs_file_path(ctx->files[i].file),
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi fs_file_last_error(ctx->files[i].file));
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi doveadm_exit_code = EX_TEMPFAIL;
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi fs_file_deinit(&ctx->files[i].file);
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi ctx->files[i].finished = TRUE;
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi }
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi }
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi return ret;
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi}
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomi
7c681809344b880aae61e99b6ab74ae19945ff9eAki Tuomistatic void
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainencmd_fs_delete_dir_recursive(struct fs *fs, unsigned int async_count,
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek const char *path_prefix)
c536bf22651c97ad0369864897f1920625e53ea9Josef 'Jeff' Sipek{
3bb59b47d47cba85a92df67823b0e49d2c383307Timo Sirainen struct fs_iter *iter;
ARRAY_TYPE(const_string) fnames;
struct fs_delete_ctx ctx;
const char *fname, *const *fnamep;
unsigned int i;
memset(&ctx, 0, sizeof(ctx));
ctx.files_count = I_MAX(async_count, 1);
ctx.files = t_new(struct fs_delete_file, ctx.files_count);
/* delete subdirs first. all fs backends can't handle recursive
lookups, so save the list first. */
t_array_init(&fnames, 8);
iter = fs_iter_init(fs, path_prefix, FS_ITER_FLAG_DIRS);
while ((fname = fs_iter_next(iter)) != NULL) {
/* append "/" so that if FS_PROPERTY_DIRECTORIES is set,
we'll include the "/" suffix in the filename when deleting
it. */
fname = t_strconcat(fname, "/", NULL);
array_append(&fnames, &fname, 1);
}
if (fs_iter_deinit(&iter) < 0) {
i_error("fs_iter_deinit(%s) failed: %s",
path_prefix, fs_last_error(fs));
doveadm_exit_code = EX_TEMPFAIL;
}
array_foreach(&fnames, fnamep) T_BEGIN {
cmd_fs_delete_dir_recursive(fs, async_count,
t_strdup_printf("%s%s", path_prefix, *fnamep));
} T_END;
/* delete files. again because we're doing this asynchronously finish
the iteration first. */
if ((fs_get_properties(fs) & FS_PROPERTY_DIRECTORIES) != 0) {
/* we need to explicitly delete also the directories */
} else {
array_clear(&fnames);
}
iter = fs_iter_init(fs, path_prefix, 0);
while ((fname = fs_iter_next(iter)) != NULL) {
fname = t_strdup(fname);
array_append(&fnames, &fname, 1);
}
if (fs_iter_deinit(&iter) < 0) {
i_error("fs_iter_deinit(%s) failed: %s",
path_prefix, fs_last_error(fs));
doveadm_exit_code = EX_TEMPFAIL;
}
array_foreach(&fnames, fnamep) T_BEGIN {
fname = *fnamep;
retry:
for (i = 0; i < ctx.files_count; i++) {
if (ctx.files[i].file != NULL || ctx.files[i].finished)
continue;
ctx.files[i].file = fs_file_init(fs,
t_strdup_printf("%s%s", path_prefix, fname),
FS_OPEN_MODE_READONLY | FS_OPEN_FLAG_ASYNC |
FS_OPEN_FLAG_ASYNC_NOQUEUE);
fname = NULL;
break;
}
cmd_fs_delete_ctx_run(&ctx);
if (fname != NULL) {
if (fs_wait_async(fs) < 0) {
i_error("fs_wait_async() failed: %s", fs_last_error(fs));
doveadm_exit_code = EX_TEMPFAIL;
break;
}
goto retry;
}
} T_END;
while (doveadm_exit_code == 0 && cmd_fs_delete_ctx_run(&ctx)) {
if (fs_wait_async(fs) < 0) {
i_error("fs_wait_async() failed: %s", fs_last_error(fs));
doveadm_exit_code = EX_TEMPFAIL;
break;
}
}
for (i = 0; i < ctx.files_count; i++) {
if (ctx.files[i].file != NULL)
fs_file_deinit(&ctx.files[i].file);
}
}
static void
cmd_fs_delete_recursive(int argc, char *argv[], unsigned int async_count)
{
struct fs *fs;
struct fs_file *file;
const char *path;
unsigned int path_len;
fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_delete);
path = argv[0];
path_len = strlen(path);
if (path_len > 0 && path[path_len-1] != '/')
path = t_strconcat(path, "/", NULL);
cmd_fs_delete_dir_recursive(fs, async_count, path);
if ((fs_get_properties(fs) & FS_PROPERTY_DIRECTORIES) != 0) {
/* delete the root itself */
file = fs_file_init(fs, path, FS_OPEN_MODE_READONLY);
if (fs_delete(file) < 0) {
i_error("fs_delete(%s) failed: %s",
fs_file_path(file), fs_file_last_error(file));
doveadm_exit_code = EX_TEMPFAIL;
}
fs_file_deinit(&file);
}
fs_deinit(&fs);
}
static void cmd_fs_delete(int argc, char *argv[])
{
struct fs *fs;
struct fs_file *file;
bool recursive = FALSE;
unsigned int async_count = 0;
int c;
while ((c = getopt(argc, argv, "Rn:")) > 0) {
switch (c) {
case 'R':
recursive = TRUE;
break;
case 'n':
if (str_to_uint(optarg, &async_count) < 0)
i_fatal("Invalid -n parameter: %s", optarg);
break;
default:
fs_cmd_help(cmd_fs_delete);
}
}
argc -= optind-1; argv += optind-1;
if (recursive) {
cmd_fs_delete_recursive(argc, argv, async_count);
return;
}
fs = cmd_fs_init(&argc, &argv, 1, cmd_fs_delete);
file = fs_file_init(fs, argv[0], FS_OPEN_MODE_READONLY);
if (fs_delete(file) == 0)
;
else if (errno == ENOENT) {
i_error("%s doesn't exist", fs_file_path(file));
doveadm_exit_code = DOVEADM_EX_NOTFOUND;
} else {
i_error("fs_delete(%s) failed: %s",
fs_file_path(file), fs_file_last_error(file));
doveadm_exit_code = EX_TEMPFAIL;
}
fs_file_deinit(&file);
fs_deinit(&fs);
}
static void cmd_fs_iter_full(int argc, char *argv[], enum fs_iter_flags flags,
doveadm_command_t *cmd)
{
struct fs *fs;
struct fs_iter *iter;
const char *fname;
fs = cmd_fs_init(&argc, &argv, 1, cmd);
iter = fs_iter_init(fs, argv[0], flags);
while ((fname = fs_iter_next(iter)) != NULL)
printf("%s\n", fname);
if (fs_iter_deinit(&iter) < 0) {
i_error("fs_iter_deinit(%s) failed: %s",
argv[0], fs_last_error(fs));
doveadm_exit_code = EX_TEMPFAIL;
}
fs_deinit(&fs);
}
static void cmd_fs_iter(int argc, char *argv[])
{
cmd_fs_iter_full(argc, argv, 0, cmd_fs_iter);
}
static void cmd_fs_iter_dirs(int argc, char *argv[])
{
cmd_fs_iter_full(argc, argv, FS_ITER_FLAG_DIRS, cmd_fs_iter_dirs);
}
struct doveadm_cmd doveadm_cmd_fs[] = {
{ cmd_fs_get, "fs get", "<fs-driver> <fs-args> <path>" },
{ cmd_fs_put, "fs put", "[-h <hash>] <fs-driver> <fs-args> <input path> <path>" },
{ cmd_fs_copy, "fs copy", "<fs-driver> <fs-args> <source path> <dest path>" },
{ cmd_fs_stat, "fs stat", "<fs-driver> <fs-args> <path>" },
{ cmd_fs_metadata, "fs metadata", "<fs-driver> <fs-args> <path>" },
{ cmd_fs_delete, "fs delete", "[-R] [-n <count>] <fs-driver> <fs-args> <path>" },
{ cmd_fs_iter, "fs iter", "<fs-driver> <fs-args> <path>" },
{ cmd_fs_iter_dirs, "fs iter-dirs", "<fs-driver> <fs-args> <path>" },
};
static void fs_cmd_help(doveadm_command_t *cmd)
{
unsigned int i;
for (i = 0; i < N_ELEMENTS(doveadm_cmd_fs); i++) {
if (doveadm_cmd_fs[i].cmd == cmd)
help(&doveadm_cmd_fs[i]);
}
i_unreached();
}
void doveadm_register_fs_commands(void)
{
unsigned int i;
for (i = 0; i < N_ELEMENTS(doveadm_cmd_fs); i++)
doveadm_register_cmd(&doveadm_cmd_fs[i]);
}