bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "lib.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "str.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "istream.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#include "ostream.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "ostream-cmp.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "fs-sis-common.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#define FS_SIS_REQUIRED_PROPS \
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen (FS_PROPERTY_FASTCOPY | FS_PROPERTY_STAT)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstruct sis_fs {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct fs fs;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen};
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstruct sis_fs_file {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct fs_file file;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen struct sis_fs *fs;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen enum fs_open_mode open_mode;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct fs_file *hash_file;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct istream *hash_input;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct ostream *fs_output;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen char *hash, *hash_path;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen bool opened;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen};
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic struct fs *fs_sis_alloc(void)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct sis_fs *fs;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs = i_new(struct sis_fs, 1);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs->fs = fs_class_sis;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return &fs->fs;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen}
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic int
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenfs_sis_init(struct fs *_fs, const char *args, const struct fs_settings *set)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen enum fs_properties props;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *parent_name, *parent_args, *error;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen if (*args == '\0') {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_set_error(_fs, "Parent filesystem not given as parameter");
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return -1;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen parent_args = strchr(args, ':');
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen if (parent_args == NULL) {
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen parent_name = args;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen parent_args = "";
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen } else {
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen parent_name = t_strdup_until(args, parent_args);
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen parent_args++;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
e597ab14b6fc01a602b35d26177d09643af8fed5Timo Sirainen if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) {
1cc7a6c6a986b972fdb7eac146f4ca8036aca4e6Timo Sirainen fs_set_error(_fs, "%s", error);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return -1;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
e597ab14b6fc01a602b35d26177d09643af8fed5Timo Sirainen props = fs_get_properties(_fs->parent);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if ((props & FS_SIS_REQUIRED_PROPS) != FS_SIS_REQUIRED_PROPS) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_set_error(_fs, "%s backend can't be used with SIS",
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen parent_name);
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return -1;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return 0;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic void fs_sis_deinit(struct fs *_fs)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct sis_fs *fs = (struct sis_fs *)_fs;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
e597ab14b6fc01a602b35d26177d09643af8fed5Timo Sirainen if (_fs->parent != NULL)
e597ab14b6fc01a602b35d26177d09643af8fed5Timo Sirainen fs_deinit(&_fs->parent);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen i_free(fs);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainenstatic struct fs_file *fs_sis_file_alloc(void)
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen{
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen struct sis_fs_file *file = i_new(struct sis_fs_file, 1);
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen return &file->file;
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen}
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainenstatic void
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainenfs_sis_file_init(struct fs_file *_file, const char *path,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen enum fs_open_mode mode, enum fs_open_flags flags)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen struct sis_fs_file *file = (struct sis_fs_file *)_file;
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen struct sis_fs *fs = (struct sis_fs *)_file->fs;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen const char *dir, *hash;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen file->file.path = i_strdup(path);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen file->fs = fs;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen file->open_mode = mode;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (mode == FS_OPEN_MODE_APPEND) {
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen fs_set_error(_file->fs, "APPEND mode not supported");
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen return;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen if (fs_sis_path_parse(_file->fs, path, &dir, &hash) < 0) {
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen fs_set_error(_file->fs, "Invalid path");
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen return;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* if hashes/<hash> already exists, open it */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen file->hash_path = i_strdup_printf("%s/"HASH_DIR_NAME"/%s", dir, hash);
6d1218e68ce883735ffde9d7907e626ab81b9fb5Timo Sirainen file->hash_file = fs_file_init_parent(_file, file->hash_path,
6d1218e68ce883735ffde9d7907e626ab81b9fb5Timo Sirainen FS_OPEN_MODE_READONLY);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen file->hash_input = fs_read_stream(file->hash_file, IO_BLOCK_SIZE);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (i_stream_read(file->hash_input) == -1) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen /* doesn't exist */
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (errno != ENOENT) {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "Couldn't read hash file %s: %m",
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen file->hash_path);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_stream_destroy(&file->hash_input);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
6d1218e68ce883735ffde9d7907e626ab81b9fb5Timo Sirainen file->file.parent = fs_file_init_parent(_file, path, mode | flags);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic void fs_sis_file_deinit(struct fs_file *_file)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct sis_fs_file *file = (struct sis_fs_file *)_file;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_file_deinit(&file->hash_file);
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_file_deinit(&_file->parent);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen i_free(file->hash);
66a164c358d14bb61de73288588ea65a6ba05113Timo Sirainen i_free(file->hash_path);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen i_free(file->file.path);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen i_free(file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
0b32a8d139f6a4f2b18a6444fc66d31b4a1b0da6Timo Sirainenstatic void fs_sis_file_close(struct fs_file *_file)
0b32a8d139f6a4f2b18a6444fc66d31b4a1b0da6Timo Sirainen{
0b32a8d139f6a4f2b18a6444fc66d31b4a1b0da6Timo Sirainen struct sis_fs_file *file = (struct sis_fs_file *)_file;
0b32a8d139f6a4f2b18a6444fc66d31b4a1b0da6Timo Sirainen
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek i_stream_unref(&file->hash_input);
0b32a8d139f6a4f2b18a6444fc66d31b4a1b0da6Timo Sirainen fs_file_close(file->hash_file);
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_file_close(_file->parent);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic bool fs_sis_try_link(struct sis_fs_file *file)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen const struct stat *st;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct stat st2;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (i_stream_stat(file->hash_input, FALSE, &st) < 0)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return FALSE;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* we can use the existing file */
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (fs_copy(file->hash_file, file->file.parent) < 0) {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen if (errno != ENOENT && errno != EMLINK) {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s",
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen fs_file_last_error(file->hash_file));
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* failed to use link(), continue as if it hadn't been equal */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return FALSE;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (fs_stat(file->file.parent, &st2) < 0) {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s",
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen fs_file_last_error(file->file.parent));
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen if (fs_delete(file->file.parent) < 0) {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s",
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen fs_file_last_error(file->file.parent));
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return FALSE;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (st->st_ino != st2.st_ino) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* the hashes/ file was already replaced with something else */
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen if (fs_delete(file->file.parent) < 0) {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s",
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen fs_file_last_error(file->file.parent));
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return FALSE;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return TRUE;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic void fs_sis_replace_hash_file(struct sis_fs_file *file)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen struct fs *super_fs = file->file.parent->fs;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen struct fs_file *temp_file;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *hash_fname;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen string_t *temp_path;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen int ret;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (file->hash_input == NULL) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* hash file didn't exist previously. we should be able to
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen create it with link() */
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (fs_copy(file->file.parent, file->hash_file) < 0) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (errno == EEXIST) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* the file was just created. it's probably
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen a duplicate, but it's too much trouble
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen trying to deduplicate it anymore */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } else {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s",
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen fs_last_error(super_fs));
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen temp_path = t_str_new(256);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen hash_fname = strrchr(file->hash_path, '/');
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (hash_fname == NULL)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen hash_fname = file->hash_path;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen else {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen str_append_n(temp_path, file->hash_path,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen (hash_fname-file->hash_path) + 1);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen hash_fname++;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen str_printfa(temp_path, "%s%s.tmp",
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen super_fs->set.temp_file_prefix, hash_fname);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* replace existing hash file atomically */
6d1218e68ce883735ffde9d7907e626ab81b9fb5Timo Sirainen temp_file = fs_file_init_parent(&file->file, str_c(temp_path),
6d1218e68ce883735ffde9d7907e626ab81b9fb5Timo Sirainen FS_OPEN_MODE_READONLY);
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen ret = fs_copy(file->file.parent, temp_file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (ret < 0 && errno == EEXIST) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* either someone's racing us or it's a stale file.
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen try to continue. */
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (fs_delete(temp_file) < 0 &&
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen errno != ENOENT)
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s", fs_last_error(super_fs));
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen ret = fs_copy(file->file.parent, temp_file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (ret < 0) {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s", fs_last_error(super_fs));
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_file_deinit(&temp_file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (fs_rename(temp_file, file->hash_file) < 0) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (errno == ENOENT) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* apparently someone else just renamed it. ignore. */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } else {
b254b8ec1ea03f400f1390ec24afd826a8c335d9Timo Sirainen e_error(file->file.event, "%s", fs_last_error(super_fs));
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen (void)fs_delete(temp_file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_file_deinit(&temp_file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic int fs_sis_write(struct fs_file *_file, const void *data, size_t size)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct sis_fs_file *file = (struct sis_fs_file *)_file;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (_file->parent == NULL)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return -1;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (file->hash_input != NULL &&
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen stream_cmp_block(file->hash_input, data, size) &&
1bc075e2e4ed422f9590c95c3ae223422b97ce6aTimo Sirainen i_stream_read_eof(file->hash_input)) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* try to use existing file */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (fs_sis_try_link(file))
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return 0;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (fs_write(_file->parent, data, size) < 0)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return -1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen T_BEGIN {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs_sis_replace_hash_file(file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } T_END;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return 0;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic void fs_sis_write_stream(struct fs_file *_file)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct sis_fs_file *file = (struct sis_fs_file *)_file;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen i_assert(_file->output == NULL);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (_file->parent == NULL) {
6516e7c2cfb84bbdaff7d748df0a0f1f6f39f75dTimo Sirainen _file->output = o_stream_create_error_str(EINVAL, "%s",
6516e7c2cfb84bbdaff7d748df0a0f1f6f39f75dTimo Sirainen fs_file_last_error(_file));
6516e7c2cfb84bbdaff7d748df0a0f1f6f39f75dTimo Sirainen } else {
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen file->fs_output = fs_write_stream(_file->parent);
b866325bc5a7b922884cf10bb0316954c12ee75dTimo Sirainen if (file->hash_input == NULL) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen _file->output = file->fs_output;
b866325bc5a7b922884cf10bb0316954c12ee75dTimo Sirainen o_stream_ref(_file->output);
b866325bc5a7b922884cf10bb0316954c12ee75dTimo Sirainen } else {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen /* compare if files are equal */
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen _file->output = o_stream_create_cmp(file->fs_output,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen file->hash_input);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen o_stream_set_name(_file->output, _file->path);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic int fs_sis_write_stream_finish(struct fs_file *_file, bool success)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct sis_fs_file *file = (struct sis_fs_file *)_file;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (!success) {
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (_file->parent != NULL)
b8f4e3aa58caa7ee16cf21a21f92521bf05cbdc4Aki Tuomi fs_write_stream_abort_parent(_file, &file->fs_output);
b866325bc5a7b922884cf10bb0316954c12ee75dTimo Sirainen o_stream_unref(&_file->output);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return -1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (file->hash_input != NULL &&
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen o_stream_cmp_equals(_file->output) &&
1bc075e2e4ed422f9590c95c3ae223422b97ce6aTimo Sirainen i_stream_read_eof(file->hash_input)) {
b866325bc5a7b922884cf10bb0316954c12ee75dTimo Sirainen o_stream_unref(&_file->output);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (fs_sis_try_link(file)) {
b8f4e3aa58caa7ee16cf21a21f92521bf05cbdc4Aki Tuomi fs_write_stream_abort_parent(_file, &file->fs_output);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return 1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
53895e43662b29a954d8f70bd2a27bafbdb4bbe8Timo Sirainen if (_file->output != NULL)
53895e43662b29a954d8f70bd2a27bafbdb4bbe8Timo Sirainen o_stream_unref(&_file->output);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen if (fs_write_stream_finish(_file->parent, &file->fs_output) < 0)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return -1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen T_BEGIN {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs_sis_replace_hash_file(file);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } T_END;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return 1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic int fs_sis_delete(struct fs_file *_file)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen T_BEGIN {
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_sis_try_unlink_hash_file(_file->fs, _file->parent);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } T_END;
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen return fs_delete(_file->parent);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
6e8fce0589289d10e6dcd9b71fde763492bb29b8Timo Sirainenconst struct fs fs_class_sis = {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen .name = "sis",
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen .v = {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_sis_alloc,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs_sis_init,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs_sis_deinit,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_get_properties,
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen fs_sis_file_alloc,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_sis_file_init,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_sis_file_deinit,
0b32a8d139f6a4f2b18a6444fc66d31b4a1b0da6Timo Sirainen fs_sis_file_close,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_file_get_path,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_set_async_callback,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_wait_async,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_set_metadata,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_get_metadata,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_prefetch,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_read,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_read_stream,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs_sis_write,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs_sis_write_stream,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fs_sis_write_stream_finish,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_lock,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_unlock,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_exists,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_stat,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_copy,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_rename,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_sis_delete,
4d71d712b42285afbd2a997f3408142ef0d5f899Timo Sirainen fs_wrapper_iter_alloc,
8b3f557cda9023b87b183e364cb7f7c7b0906950Timo Sirainen fs_wrapper_iter_init,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen NULL,
8296531314913c7f9d4ab1857c6f79ff1308a12fTimo Sirainen NULL,
a63cd84128875485e40ed804dbf0b0945526989cTimo Sirainen NULL,
a63cd84128875485e40ed804dbf0b0945526989cTimo Sirainen fs_wrapper_get_nlinks,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen};