static bool fs_sis_try_link(struct sis_fs_file *file) { const char *path = fs_file_path(&file->file); const struct stat *st; struct stat st2; st = i_stream_stat(file->hash_input, FALSE); /* we can use the existing file */ if (fs_link(file->super->fs, file->hash_path, path) < 0) { if (errno != ENOENT && errno != EMLINK) i_error("fs-sis: %s", fs_last_error(file->super->fs)); /* failed to use link(), continue as if it hadn't been equal */ return FALSE; } if (fs_stat(file->super->fs, path, &st2) < 0) { i_error("fs-sis: %s", fs_last_error(file->super->fs)); if (fs_unlink(file->super->fs, path) < 0) i_error("fs-sis: %s", fs_last_error(file->super->fs)); return FALSE; } if (st->st_ino != st2.st_ino) { /* the hashes/ file was already replaced with something else */ if (fs_unlink(file->super->fs, path) < 0) i_error("fs-sis: %s", fs_last_error(file->super->fs)); return FALSE; } return TRUE; }
static void fs_sis_replace_hash_file(struct sis_fs_file *file) { const char *hash_fname, *path = fs_file_path(&file->file); struct fs *super_fs = file->super->fs; string_t *temp_path; int ret; if (file->hash_input == NULL) { /* hash file didn't exist previously. we should be able to create it with link() */ if (fs_link(super_fs, path, file->hash_path) < 0) { if (errno == EEXIST) { /* the file was just created. it's probably a duplicate, but it's too much trouble trying to deduplicate it anymore */ } else { i_error("fs-sis: %s", fs_last_error(super_fs)); } } return; } temp_path = t_str_new(256); hash_fname = strrchr(file->hash_path, '/'); if (hash_fname == NULL) hash_fname = file->hash_path; else { str_append_n(temp_path, file->hash_path, (hash_fname-file->hash_path) + 1); hash_fname++; } str_printfa(temp_path, "%s%s.tmp", super_fs->set.temp_file_prefix, hash_fname); /* replace existing hash file atomically */ ret = fs_link(super_fs, path, str_c(temp_path)); if (ret < 0 && errno == EEXIST) { /* either someone's racing us or it's a stale file. try to continue. */ if (fs_unlink(super_fs, str_c(temp_path)) < 0 && errno != ENOENT) i_error("fs-sis: %s", fs_last_error(super_fs)); ret = fs_link(super_fs, path, str_c(temp_path)); } if (ret < 0) { i_error("fs-sis: %s", fs_last_error(super_fs)); return; } if (fs_rename(super_fs, str_c(temp_path), file->hash_path) < 0) { if (errno == ENOENT) { /* apparently someone else just renamed it. ignore. */ } else { i_error("fs-sis: %s", fs_last_error(super_fs)); } (void)fs_unlink(super_fs, str_c(temp_path)); } }
static int fs_sis_open(struct fs *_fs, const char *path, enum fs_open_mode mode, enum fs_open_flags flags, struct fs_file **file_r) { struct sis_fs *fs = (struct sis_fs *)_fs; struct sis_fs_file *file; struct fs_file *super; const char *dir, *hash; if (mode == FS_OPEN_MODE_APPEND) { fs_set_error(_fs, "APPEND mode not supported"); return -1; } if (fs_open(fs->super, path, mode | flags, &super) < 0) { fs_sis_copy_error(fs); return -1; } switch (mode) { case FS_OPEN_MODE_RDONLY: *file_r = super; return 0; case FS_OPEN_MODE_CREATE: case FS_OPEN_MODE_REPLACE: break; case FS_OPEN_MODE_APPEND: i_unreached(); } if (fs_sis_path_parse(_fs, path, &dir, &hash) < 0) return -1; file = i_new(struct sis_fs_file, 1); file->file.fs = _fs; file->file.path = i_strdup(fs_file_path(super)); file->super = super; file->open_mode = mode; file->hash = i_strdup(hash); /* if hashes/<hash> already exists, open it */ file->hash_path = i_strdup_printf("%s/"HASH_DIR_NAME"/%s", dir, hash); if (fs_open(fs->super, file->hash_path, FS_OPEN_MODE_RDONLY, &file->hash_file) < 0 && errno != ENOENT) { i_error("fs-sis: Couldn't open hash file: %s", fs_last_error(fs->super)); } if (file->hash_file != NULL) { file->hash_input = fs_read_stream(file->hash_file, IO_BLOCK_SIZE); } *file_r = &file->file; return 0; }
static void fs_compress_write_stream(struct fs_file *_file) { struct compress_fs_file *file = (struct compress_fs_file *)_file; 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->handler-> create_ostream(file->temp_output, file->fs->compress_level); }
static void fs_crypt_write_stream(struct fs_file *_file) { struct crypt_fs_file *file = (struct crypt_fs_file *)_file; const char *error; i_assert(_file->output == NULL); if (fs_crypt_load_keys(file->fs, &error) < 0) { _file->output = o_stream_create_error_str(EIO, "Couldn't read settings: %s", error); return; } if (file->fs->keys.public_key == NULL) { if (_file->fs->set.debug) i_debug("No public key provided, " "NOT encrypting stream %s", fs_file_path(_file)); file->super_output = fs_write_stream(_file->parent); _file->output = file->super_output; return; } enum io_stream_encrypt_flags flags; if (strstr(file->fs->enc_algo, "gcm") != NULL || strstr(file->fs->enc_algo, "ccm") != NULL) { flags = IO_STREAM_ENC_INTEGRITY_AEAD; } else { flags = IO_STREAM_ENC_INTEGRITY_HMAC; } file->temp_output = iostream_temp_create_named(_file->fs->temp_path_prefix, IOSTREAM_TEMP_FLAG_TRY_FD_DUP, fs_file_path(_file)); _file->output = o_stream_create_encrypt(file->temp_output, file->fs->enc_algo, file->fs->keys.public_key, flags); }
struct istream * i_stream_create_fs_file(struct fs_file **file, size_t max_buffer_size) { struct fs_file_istream *fstream; struct istream *input; fstream = i_new(struct fs_file_istream, 1); fstream->file = *file; fstream->istream.iostream.close = i_stream_fs_file_close; fstream->istream.max_buffer_size = max_buffer_size; fstream->istream.read = i_stream_fs_file_read; fstream->istream.stream_size_passthrough = TRUE; fstream->istream.istream.blocking = ((*file)->flags & FS_OPEN_FLAG_ASYNC) == 0; fstream->istream.istream.seekable = ((*file)->flags & FS_OPEN_FLAG_SEEKABLE) != 0; input = i_stream_create(&fstream->istream, NULL, -1); i_stream_set_name(input, fs_file_path(*file)); *file = NULL; return input; }
static const char *fs_sis_queue_file_get_path(struct fs_file *_file) { struct sis_queue_fs_file *file = (struct sis_queue_fs_file *)_file; return fs_file_path(file->super); }
const char *fs_wrapper_file_get_path(struct fs_file *file) { return fs_file_path(file->parent); }
static const char *fs_compress_file_get_path(struct fs_file *_file) { struct compress_fs_file *file = (struct compress_fs_file *)_file; return fs_file_path(file->super); }