acl-backend-vfile.c revision 5f1124cbd48c2c36dd8061e5c3e028faeaf62dca
/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "str.h"
#include "istream.h"
#include "ostream.h"
#include "file-dotlock.h"
#include "nfs-workarounds.h"
#include "mail-storage-private.h"
#include "mail-namespace.h"
#include "acl-cache.h"
#include "acl-backend-vfile.h"
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define VALIDITY_MTIME_NOTFOUND 0
#define VALIDITY_MTIME_NOACCESS -1
struct acl_vfile_validity {
};
struct acl_backend_vfile_validity {
struct acl_vfile_validity mailbox_validity;
};
struct acl_letter_map {
char letter;
const char *name;
};
static const struct acl_letter_map acl_letter_map[] = {
{ 'l', MAIL_ACL_LOOKUP },
{ 'r', MAIL_ACL_READ },
{ 'w', MAIL_ACL_WRITE },
{ 's', MAIL_ACL_WRITE_SEEN },
{ 't', MAIL_ACL_WRITE_DELETED },
{ 'i', MAIL_ACL_INSERT },
{ 'e', MAIL_ACL_EXPUNGE },
{ 'k', MAIL_ACL_CREATE },
{ 'x', MAIL_ACL_DELETE },
{ 'a', MAIL_ACL_ADMIN },
{ '\0', NULL }
};
static struct dotlock_settings dotlock_set = {
};
static struct acl_backend *acl_backend_vfile_alloc(void)
{
struct acl_backend_vfile *backend;
}
static int
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
const char *const *tmp;
tmp++;
else {
return -1;
}
}
i_info("acl vfile: Global ACL directory: %s",
}
sizeof(struct acl_backend_vfile_validity));
return 0;
}
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
}
}
static const char *
{
const char *dir;
bool is_file;
if (is_file) {
}
return dir;
}
static struct acl_object *
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
struct acl_object_vfile *aclobj;
const char *dir;
/* the default ACL for mailbox list */
} else {
}
}
static const char *
{
const char *p;
char sep;
}
static int
struct acl_vfile_validity *validity)
{
/* use the cached value */
}
return 0;
}
return 1;
}
return -1;
}
return 1;
}
static bool
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
int ret;
if (old_validity != NULL)
else
/* See if the mailbox exists. If we wanted recursive lookups we could
skip this, but at least for now we assume that if an existing
mailbox has no ACL it's equivalent to default ACLs. */
if (ret == 0) {
}
}
return ret > 0;
}
static struct acl_object *
struct mail_storage *storage,
const char *child_name)
{
const char *parent;
/* stop at the first parent that
a) has global ACL file
b) has local ACL file
c) exists */
break;
child_name = parent;
}
/* use the root */
parent = "";
}
}
{
}
static const char *const *
bool dup_strings)
{
/* sort the rights first so we can easily drop duplicates */
/* @UNSAFE */
if (count > 0) {
}
if (dup_strings) {
for (i = 0; i < dest; i++)
}
}
return ret;
}
static const char *const *
{
const char *const *names;
unsigned int i;
/* parse IMAP ACL list */
acl++;
break;
}
return NULL;
}
acl++;
}
if (*acl != '\0') {
/* parse our own extended ACLs */
}
}
}
static int
unsigned int linenum)
{
struct acl_rights rights;
return 0;
/* <id> [<imap acls>] [:<named acls>] */
if (p == NULL)
p = "";
else {
p++;
}
if (*line != '-') {
} else {
line++;
}
switch (*line) {
case 'u':
strlen(ACL_ID_NAME_USER_PREFIX)) == 0) {
break;
}
case 'o':
break;
}
case 'g':
strlen(ACL_ID_NAME_GROUP_PREFIX)) == 0) {
break;
strlen(ACL_ID_NAME_GROUP_OVERRIDE_PREFIX)) == 0) {
break;
}
case 'a':
break;
break;
}
default:
break;
}
return -1;
}
return 0;
}
{
struct acl_rights_update rights;
}
static int
bool *is_dir_r)
{
const char *line;
unsigned int linenum;
if (fd == -1) {
} else {
return -1;
}
return 1;
}
return 0;
}
return -1;
}
/* we opened a directory. */
return 0;
}
linenum = 1;
T_BEGIN {
linenum++);
} T_END;
if (ret < 0)
break;
}
if (ret < 0) {
/* parsing failure */
} else if (input->stream_errno != 0) {
ret = 0;
else {
ret = -1;
}
} else {
ret = 0;
else {
ret = -1;
}
} else {
ret = 1;
}
}
return 0;
return -1;
}
return ret;
}
static int
struct acl_vfile_validity *validity)
{
unsigned int i;
int ret;
bool is_dir;
return 0;
for (i = 0;; i++) {
&is_dir);
if (ret != 0)
break;
if (is_dir) {
/* opened a directory. use dir/.DEFAULT instead */
} else {
/* ESTALE - try again */
}
}
return ret <= 0 ? -1 : 0;
}
static int
struct acl_vfile_validity *validity)
{
struct acl_backend_vfile *backend =
return 1;
return 0;
/* if the file used to exist, we have to re-read it */
}
return -1;
}
/* same timestamp, but if it was modified within the
same second we want to refresh it again later (but
do it only after a couple of seconds so we don't
keep re-reading it all the time within those
seconds) */
if (validity->last_read_time != 0 &&
return 0;
}
return 1;
}
{
struct acl_backend_vfile_validity *validity;
return -1;
else
*mtime_r = 0;
return 0;
}
{
int ret;
/* globals have higher priority than locals */
}
if (ret != 0)
return ret;
}
static void
bool dup_strings)
{
unsigned int i;
}
}
}
{
struct acl_rights *rights;
return;
/* merge identical identifiers */
/* add i's rights to dest and delete i */
} else {
if (++dest != i)
}
}
}
{
struct acl_rights_update ru;
}
{
struct mail_namespace *ns;
const struct acl_rights *rights;
unsigned int i, count;
return;
for (i = 0; i < count; i++) {
if (!owner_applied &&
/* owner rights weren't explicitly specified.
replace all the current rights */
}
}
/* If [neg_]rights is NULL it needs to be ignored.
The easiest way to do that is to just mark it with
REMOVE mode */
/* first global: reset negative ACLs so local ACLs
can't mess things up via them */
/* make sure owner has admin rights
(at least before global ACLs are applied) */
}
}
}
if (!owner_applied && count > 0)
}
{
struct acl_backend_vfile *backend =
struct acl_backend_vfile_validity *old_validity;
struct acl_backend_vfile_validity validity;
int ret;
if (ret == 0) {
}
if (ret <= 0)
return ret;
/* either global or local ACLs changed, need to re-read both */
} else {
}
&validity.global_validity) < 0)
return -1;
&validity.local_validity) < 0)
return -1;
/* update cache only after we've successfully read everything */
return 0;
}
{
int fd;
/* first lock the ACL file */
if (fd == -1) {
i_error("file_dotlock_open_mode(%s) failed: %m",
aclobj->local_path);
return -1;
}
/* locked successfully, re-read the existing file to make sure we
don't lose any changes. */
if (acl_backend_vfile_object_refresh_cache(_aclobj) < 0) {
return -1;
}
return fd;
}
const char *const **rightsp,
const char *const *modify_rights,
enum acl_modify_mode modify_mode)
{
const char *const *old_rights = *rightsp;
const char *const *new_rights;
unsigned int i, j;
/* nothing to do here */
return FALSE;
}
if (old_rights == NULL)
old_rights = &null;
switch (modify_mode) {
case ACL_MODIFY_MODE_REMOVE:
if (*old_rights == NULL) {
/* nothing to do */
return FALSE;
}
for (i = 0; old_rights[i] != NULL; i++) {
for (j = 0; modify_rights[j] != NULL; j++) {
break;
}
if (modify_rights[j] == NULL)
}
new_rights = &null;
break;
case ACL_MODIFY_MODE_ADD:
break;
case ACL_MODIFY_MODE_REPLACE:
new_rights = &null;
break;
case ACL_MODIFY_MODE_CLEAR:
/* ACL didn't exist before either */
return FALSE;
}
return TRUE;
}
*rightsp = new_rights;
/* see if anything changed */
return TRUE;
}
}
static bool
const struct acl_rights_update *update)
{
struct acl_rights *right;
/* this identifier no longer exists */
}
}
static bool
const struct acl_rights_update *update)
{
struct acl_rights right;
/* nothing to do */
return FALSE;
}
}
{
char c2[2];
unsigned int i, j, pos;
/* use letters if possible */
pos++;
break;
}
}
/* fallback to full name */
}
}
}
static void
bool neg)
{
case ACL_ID_ANYONE:
break;
case ACL_ID_AUTHENTICATED:
break;
case ACL_ID_OWNER:
break;
case ACL_ID_USER:
break;
case ACL_ID_GROUP:
break;
case ACL_ID_GROUP_OVERRIDE:
break;
case ACL_ID_TYPE_COUNT:
i_unreached();
}
}
static int
{
const struct acl_rights *rights;
unsigned int i, count;
int ret = 0;
/* rights are sorted with globals at the end, so we can stop at the
first global */
str_truncate(str, 0);
}
if (o_stream_flush(output) < 0) {
ret = -1;
}
/* we really don't want to lose ACL files' contents, so fsync() always
before renaming */
ret = -1;
}
return ret;
}
{
struct acl_backend_vfile_validity *validity;
/* we'll just recalculate or fail it later */
return;
}
}
static int
const struct acl_rights_update *update)
{
const struct acl_rights *rights;
const char *path;
unsigned int i, count;
int fd;
bool changed;
/* global ACLs can't be updated here */
if (fd == -1)
return -1;
acl_rights_cmp, &i))
else
if (!changed) {
return 0;
} else {
return -1;
}
if (file_dotlock_replace(&dotlock, 0) < 0) {
return -1;
}
return 0;
}
}
static struct acl_object_list_iter *
{
struct acl_object_vfile *aclobj =
(struct acl_object_vfile *)_aclobj;
struct acl_object_list_iter *iter;
struct mail_namespace *ns;
/* we may have the object cached, but we don't have all the
rights read into memory */
}
/* be sure to return owner for private namespaces.
(other namespaces don't have an owner) */
return iter;
}
static int
struct acl_rights *rights_r)
{
struct acl_object_vfile *aclobj =
const struct acl_rights *rights;
if (iter->returned_owner)
return 0;
/* return missing owner based on the default ACLs */
return 1;
}
return 1;
}
static void
{
}
struct acl_backend_vfuncs acl_backend_vfile = {
};