20
21
struct acl_mailbox_list_context_vfile {
21
22
struct acl_mailbox_list_context ctx;
23
unsigned int acllist_change_counter;
28
28
acllist_clear(struct acl_backend_vfile *backend, uoff_t file_size)
30
backend->acllist_change_counter++;
31
30
if (backend->acllist_pool == NULL) {
32
31
backend->acllist_pool =
33
32
pool_alloconly_create("vfile acllist",
41
static const char *acl_list_get_root_dir(struct acl_backend_vfile *backend)
43
struct mail_storage *storage;
44
const char *rootdir, *maildir;
47
rootdir = mailbox_list_get_path(backend->backend.list, NULL,
48
MAILBOX_LIST_PATH_TYPE_DIR);
50
storage = mailbox_list_get_namespace(backend->backend.list)->storage;
51
(void)mail_storage_get_mailbox_path(storage, "", &is_file);
53
maildir = mailbox_list_get_path(backend->backend.list, NULL,
54
MAILBOX_LIST_PATH_TYPE_MAILBOX);
55
if (strcmp(maildir, rootdir) == 0) {
56
/* dovecot-acl-list would show up as a mailbox if we
57
created it to root dir. since we don't really have
58
any other good alternatives, place it to control
60
rootdir = mailbox_list_get_path(backend->backend.list,
61
NULL, MAILBOX_LIST_PATH_TYPE_CONTROL);
67
static const char *acl_list_get_path(struct acl_backend_vfile *backend)
69
return t_strconcat(acl_list_get_root_dir(backend),
70
"/"ACLLIST_FILENAME, NULL);
42
73
static int acl_backend_vfile_acllist_read(struct acl_backend_vfile *backend)
44
75
struct acl_backend_vfile_acllist acllist;
45
76
struct istream *input;
47
const char *rootdir, *path, *line, *p;
78
const char *path, *line, *p;
50
81
backend->acllist_last_check = ioloop_time;
52
rootdir = mailbox_list_get_path(backend->backend.list, NULL,
53
MAILBOX_LIST_PATH_TYPE_DIR);
54
path = t_strdup_printf("%s/"ACLLIST_FILENAME, rootdir);
83
path = acl_list_get_path(backend);
85
/* we're never going to build acllist for this namespace. */
86
i_array_init(&backend->acllist, 1);
56
90
if (backend->acllist_mtime != 0) {
57
91
/* see if the file's mtime has changed */
123
static bool rights_has_lookup_changes(const struct acl_rights *rights)
125
const char *const *p;
127
if (rights->id_type == ACL_ID_OWNER) {
128
/* ignore owner rights */
132
if (rights->rights == NULL)
135
for (p = rights->rights; *p != NULL; p++) {
136
if (strcmp(*p, MAIL_ACL_LOOKUP) == 0)
143
160
acllist_append(struct acl_backend_vfile *backend, struct ostream *output,
144
161
struct mail_storage *storage, const char *name)
155
172
iter = acl_object_list_init(aclobj);
156
173
while ((ret = acl_object_list_next(iter, &rights)) > 0) {
157
if (rights_has_lookup_changes(&rights))
174
if (acl_rights_has_nonowner_lookup_changes(&rights))
160
177
acl_object_list_deinit(&iter);
177
194
return ret < 0 ? -1 : 0;
180
int acl_backend_vfile_acllist_rebuild(struct acl_backend_vfile *backend)
198
acl_backend_vfile_acllist_try_rebuild(struct acl_backend_vfile *backend)
182
200
struct mailbox_list *list = backend->backend.list;
183
201
struct mail_namespace *ns;
184
202
struct mailbox_list_iterate_context *iter;
185
203
const struct mailbox_info *info;
186
const char *rootdir, *acllist_path;
204
const char *rootdir, *origin, *acllist_path;
187
205
struct ostream *output;
194
mailbox_list_get_permissions(list, &mode, &gid);
212
i_assert(!backend->rebuilding_acllist);
214
rootdir = acl_list_get_root_dir(backend);
218
ns = mailbox_list_get_namespace(list);
219
if ((ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0) {
220
/* we can't write anything here */
196
224
path = t_str_new(256);
197
rootdir = mailbox_list_get_path(list, NULL,
198
MAILBOX_LIST_PATH_TYPE_DIR);
199
225
str_printfa(path, "%s/%s", rootdir, mailbox_list_get_temp_prefix(list));
201
227
/* Build it into a temporary file and rename() over. There's no need
202
228
to use locking, because even if multiple processes are rebuilding
203
229
the file at the same time the result should be the same. */
204
fd = safe_mkstemp(path, mode, (uid_t)-1, gid);
230
mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
231
fd = safe_mkstemp_group(path, mode, gid, origin);
232
if (fd == -1 && errno == ENOENT) {
233
if (mailbox_list_create_parent_dir(backend->backend.list, NULL,
236
fd = safe_mkstemp_group(path, mode, gid, origin);
206
239
if (errno == EACCES) {
207
240
/* Ignore silently if we can't create it */
217
250
acllist_clear(backend, 0);
218
ns = mailbox_list_get_namespace(list);
220
252
backend->rebuilding_acllist = TRUE;
221
253
iter = mailbox_list_iter_init(list, "*", MAILBOX_LIST_ITER_RAW_LIST |
263
if (output->stream_errno != 0) {
264
i_error("write(%s) failed: %m", str_c(path));
231
267
if (mailbox_list_iter_deinit(&iter) < 0)
233
269
o_stream_destroy(&output);
234
backend->rebuilding_acllist = FALSE;
237
272
if (fstat(fd, &st) < 0) {
248
acllist_path = t_strdup_printf("%s/"ACLLIST_FILENAME, rootdir);
283
acllist_path = acl_list_get_path(backend);
249
284
if (rename(str_c(path), acllist_path) < 0) {
250
285
i_error("rename(%s, %s) failed: %m",
251
286
str_c(path), acllist_path);
291
struct acl_user *auser = ACL_USER_CONTEXT(ns->user);
256
293
backend->acllist_mtime = st.st_mtime;
257
294
backend->acllist_last_check = ioloop_time;
295
/* FIXME: dict reubild is expensive, try to avoid it */
296
(void)acl_lookup_dict_rebuild(auser->acl_lookup_dict);
259
298
acllist_clear(backend, 0);
260
299
if (unlink(str_c(path)) < 0 && errno != ENOENT)
261
300
i_error("unlink(%s) failed: %m", str_c(path));
302
backend->rebuilding_acllist = FALSE;
306
int acl_backend_vfile_acllist_rebuild(struct acl_backend_vfile *backend)
308
const char *acllist_path;
310
if (acl_backend_vfile_acllist_try_rebuild(backend) == 0)
313
/* delete it to make sure it gets rebuilt later */
314
acllist_path = acl_list_get_path(backend);
315
if (unlink(acllist_path) < 0 && errno != ENOENT)
316
i_error("unlink(%s) failed: %m", acllist_path);
266
321
static const struct acl_backend_vfile_acllist *
267
322
acl_backend_vfile_acllist_find(struct acl_backend_vfile *backend,
268
323
const char *name)
284
339
const struct acl_backend_vfile_acllist *acllist;
341
if (backend->rebuilding_acllist || backend->iterating_acllist)
286
344
acl_backend_vfile_acllist_refresh(backend);
287
345
acllist = acl_backend_vfile_acllist_find(backend, name);
288
if (acllist != NULL && acllist->mtime != mtime &&
289
!backend->rebuilding_acllist)
346
if (acllist != NULL && acllist->mtime != mtime)
290
347
(void)acl_backend_vfile_acllist_rebuild(backend);