Coverage Report

Created: 2026-10-28 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-fs/fs-sis-queue.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "istream.h"
6
#include "ostream.h"
7
#include "settings.h"
8
#include "fs-sis-common.h"
9
10
struct fs_sis_queue_settings {
11
  pool_t pool;
12
  const char *fs_sis_queue_path;
13
};
14
15
struct sis_queue_fs {
16
  struct fs fs;
17
  char *queue_dir;
18
};
19
20
struct sis_queue_fs_file {
21
  struct fs_file file;
22
  struct sis_queue_fs *fs;
23
};
24
25
0
#define SISQUEUE_FS(ptr)  container_of((ptr), struct sis_queue_fs, fs)
26
0
#define SISQUEUE_FILE(ptr)  container_of((ptr), struct sis_queue_fs_file, file)
27
28
#undef DEF
29
#define DEF(type, name) \
30
  SETTING_DEFINE_STRUCT_##type(#name, name, struct fs_sis_queue_settings)
31
static const struct setting_define fs_sis_queue_setting_defines[] = {
32
  DEF(STR, fs_sis_queue_path),
33
34
  SETTING_DEFINE_LIST_END
35
};
36
static const struct fs_sis_queue_settings fs_sis_queue_default_settings = {
37
  .fs_sis_queue_path = "",
38
};
39
40
const struct setting_parser_info fs_sis_queue_setting_parser_info = {
41
  .name = "fs_sis_queue",
42
43
  .defines = fs_sis_queue_setting_defines,
44
  .defaults = &fs_sis_queue_default_settings,
45
46
  .struct_size = sizeof(struct fs_sis_queue_settings),
47
  .pool_offset1 = 1 + offsetof(struct fs_sis_queue_settings, pool),
48
};
49
50
static struct fs *fs_sis_queue_alloc(void)
51
0
{
52
0
  struct sis_queue_fs *fs;
53
54
0
  fs = i_new(struct sis_queue_fs, 1);
55
0
  fs->fs = fs_class_sis_queue;
56
0
  return &fs->fs;
57
0
}
58
59
static int
60
fs_sis_queue_init(struct fs *_fs, const struct fs_parameters *params,
61
      const char **error_r)
62
0
{
63
0
  struct sis_queue_fs *fs = SISQUEUE_FS(_fs);
64
0
  const struct fs_sis_queue_settings *set;
65
66
0
  if (settings_get(_fs->event, &fs_sis_queue_setting_parser_info, 0,
67
0
       &set, error_r) < 0)
68
0
    return -1;
69
0
  fs->queue_dir = i_strdup(set->fs_sis_queue_path);
70
0
  settings_free(set);
71
72
0
  return fs_init_parent(_fs, params, error_r);
73
0
}
74
75
static void fs_sis_queue_free(struct fs *_fs)
76
0
{
77
0
  struct sis_queue_fs *fs = SISQUEUE_FS(_fs);
78
79
0
  i_free(fs->queue_dir);
80
0
  i_free(fs);
81
0
}
82
83
static struct fs_file *fs_sis_queue_file_alloc(void)
84
0
{
85
0
  struct sis_queue_fs_file *file = i_new(struct sis_queue_fs_file, 1);
86
0
  return &file->file;
87
0
}
88
89
static void
90
fs_sis_queue_file_init(struct fs_file *_file, const char *path,
91
           enum fs_open_mode mode, enum fs_open_flags flags)
92
0
{
93
0
  struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
94
0
  struct sis_queue_fs *fs = SISQUEUE_FS(_file->fs);
95
96
0
  file->file.path = i_strdup(path);
97
0
  file->fs = fs;
98
99
0
  if (mode == FS_OPEN_MODE_APPEND)
100
0
    fs_set_error(_file->event, ENOTSUP, "APPEND mode not supported");
101
0
  else
102
0
    file->file.parent = fs_file_init_parent(_file, path, mode, flags);
103
0
}
104
105
static void fs_sis_queue_file_deinit(struct fs_file *_file)
106
0
{
107
0
  struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
108
109
0
  fs_file_free(_file);
110
0
  i_free(file->file.path);
111
0
  i_free(file);
112
0
}
113
114
static void fs_sis_queue_add(struct sis_queue_fs_file *file)
115
0
{
116
0
  struct sis_queue_fs *fs = SISQUEUE_FS(file->file.fs);
117
0
  struct fs_file *queue_file;
118
0
  const char *fname, *path, *queue_path;
119
120
0
  path = fs_file_path(&file->file);
121
0
  fname = strrchr(path, '/');
122
0
  if (fname != NULL)
123
0
    fname++;
124
0
  else
125
0
    fname = path;
126
127
0
  queue_path = t_strdup_printf("%s/%s", fs->queue_dir, fname);
128
0
  queue_file = fs_file_init_parent(&file->file, queue_path, FS_OPEN_MODE_CREATE, 0);
129
0
  if (fs_write(queue_file, "", 0) < 0 && errno != EEXIST)
130
0
    e_error(file->file.event, "%s", fs_file_last_error(queue_file));
131
0
  fs_file_deinit(&queue_file);
132
0
}
133
134
static int fs_sis_queue_write(struct fs_file *_file, const void *data, size_t size)
135
0
{
136
0
  struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
137
138
0
  if (_file->parent == NULL)
139
0
    return -1;
140
0
  if (fs_write(_file->parent, data, size) < 0)
141
0
    return -1;
142
0
  T_BEGIN {
143
0
    fs_sis_queue_add(file);
144
0
  } T_END;
145
0
  return 0;
146
0
}
147
148
static void fs_sis_queue_write_stream(struct fs_file *_file)
149
0
{
150
0
  i_assert(_file->output == NULL);
151
152
0
  if (_file->parent == NULL) {
153
0
    _file->output = o_stream_create_error_str(EINVAL, "%s",
154
0
            fs_file_last_error(_file));
155
0
  } else {
156
0
    _file->output = fs_write_stream(_file->parent);
157
0
  }
158
0
  o_stream_set_name(_file->output, _file->path);
159
0
}
160
161
static int fs_sis_queue_write_stream_finish(struct fs_file *_file, bool success)
162
0
{
163
0
  struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
164
165
0
  if (!success) {
166
0
    if (_file->parent != NULL)
167
0
      fs_write_stream_abort_parent(_file, &_file->output);
168
0
    return -1;
169
0
  }
170
171
0
  if (fs_write_stream_finish(_file->parent, &_file->output) < 0)
172
0
    return -1;
173
0
  T_BEGIN {
174
0
    fs_sis_queue_add(file);
175
0
  } T_END;
176
0
  return 1;
177
0
}
178
179
static int fs_sis_queue_delete(struct fs_file *_file)
180
0
{
181
0
  T_BEGIN {
182
0
    fs_sis_try_unlink_hash_file(_file, _file->parent);
183
0
  } T_END;
184
0
  return fs_delete(_file->parent);
185
0
}
186
187
const struct fs fs_class_sis_queue = {
188
  .name = "sis-queue",
189
  .v = {
190
    .alloc = fs_sis_queue_alloc,
191
    .init = fs_sis_queue_init,
192
    .deinit = NULL,
193
    .free = fs_sis_queue_free,
194
    .get_properties = fs_wrapper_get_properties,
195
    .file_alloc = fs_sis_queue_file_alloc,
196
    .file_init = fs_sis_queue_file_init,
197
    .file_deinit = fs_sis_queue_file_deinit,
198
    .file_close = fs_wrapper_file_close,
199
    .get_path = fs_wrapper_file_get_path,
200
    .set_async_callback = fs_wrapper_set_async_callback,
201
    .wait_async = fs_wrapper_wait_async,
202
    .set_metadata = fs_wrapper_set_metadata,
203
    .get_metadata = fs_wrapper_get_metadata,
204
    .prefetch = fs_wrapper_prefetch,
205
    .read = fs_wrapper_read,
206
    .read_stream = fs_wrapper_read_stream,
207
    .write = fs_sis_queue_write,
208
    .write_stream = fs_sis_queue_write_stream,
209
    .write_stream_finish = fs_sis_queue_write_stream_finish,
210
    .lock = fs_wrapper_lock,
211
    .unlock = fs_wrapper_unlock,
212
    .exists = fs_wrapper_exists,
213
    .stat = fs_wrapper_stat,
214
    .copy = fs_wrapper_copy,
215
    .rename = fs_wrapper_rename,
216
    .delete_file = fs_sis_queue_delete,
217
    .iter_alloc = fs_wrapper_iter_alloc,
218
    .iter_init = fs_wrapper_iter_init,
219
    .iter_next = NULL,
220
    .iter_deinit = NULL,
221
    .switch_ioloop = NULL,
222
    .get_nlinks = fs_wrapper_get_nlinks,
223
  }
224
};