maildir-storage.c revision 370b60cfccff7c50586fc30f4f591499a55301a8
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "hostpid.h"
#include "str.h"
#include "mkdir-parents.h"
#include "eacces-error.h"
#include "unlink-directory.h"
#include "unlink-old-files.h"
#include "mailbox-uidvalidity.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-keywords.h"
#include "maildir-sync.h"
#include "index-mail.h"
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
#define MAILDIR_LIST_CONTEXT(obj) \
struct maildir_mailbox_list {
const struct maildir_settings *set;
};
struct rename_context {
bool found;
const char *newname;
};
extern struct mail_storage maildir_storage;
extern struct mailbox maildir_mailbox;
static bool maildir_is_internal_name(const char *name)
{
}
const char *name)
{
const char *p;
return FALSE;
if (p != NULL)
name = p + 1;
return !maildir_is_internal_name(name);
}
const char *name)
{
return FALSE;
T_BEGIN {
const char *const *tmp;
if (maildir_is_internal_name(*tmp)) {
break;
}
}
} T_END;
return ret;
}
static struct mail_storage *maildir_storage_alloc(void)
{
struct maildir_storage *storage;
}
static int
const char **error_r ATTR_UNUSED)
{
const char *dir;
/* put the temp files into tmp/ directory preferrably */
} else {
/* control dir should also be writable */
}
return 0;
}
struct mailbox_list_settings *set)
{
/* Maildir++ INBOX is the Maildir base itself */
}
}
static const char *
{
/* we'll need to figure out the maildir location ourself.
It's ~/Maildir unless we are chrooted. */
if (debug)
return path;
}
if (debug)
} else {
if (debug)
i_info("maildir: Home directory not set");
if (debug)
i_info("maildir: /cur exists, assuming chroot");
return "/";
}
}
return NULL;
}
struct mailbox_list_settings *set)
{
else {
if (debug)
i_info("maildir: couldn't find root dir");
return FALSE;
}
}
if (debug)
return FALSE;
}
if (debug)
return FALSE;
}
return TRUE;
}
static int
bool verify)
{
if (verify) {
return 0;
"stat(%s) failed: %m", dir);
return -1;
}
}
return 0;
if (verify)
return 0;
"Mailbox already exists");
"Mailbox was deleted while it was being created");
/* shared namespace, don't log permission errors */
return -1;
}
} else {
"mkdir(%s) failed: %m", dir);
}
return -1;
}
{
const char *path;
/* if tmp/ directory exists, we need to clean it up once in a while */
return 0;
return -1;
}
return -1;
}
/* the directory should be empty. we won't do anything
until ctime changes. */
/* time to scan */
}
return 1;
}
/* create or fix maildir, ignore if it already exists */
static int
bool verify)
{
const char *path;
unsigned int i;
int ret;
if (!verify) {
if (ret > 0) {
MAIL_ERROR_EXISTS, "Mailbox already exists");
return -1;
}
if (ret < 0)
return -1;
}
for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
gid_origin, verify) < 0)
return -1;
}
return 0;
}
{
}
{
/* add the execute bit if either read or write bit is set */
return mode;
}
static struct mailbox *
enum mailbox_flags flags)
{
struct maildir_mailbox *mbox;
sizeof(mbox->maildir_hdr), 0, 0);
}
{
const char *shared_path;
/* for shared mailboxes get the create mode from the
permissions of dovecot-shared file. */
/* Ignore GID */
}
}
return -1;
mbox);
}
return index_storage_mailbox_open(box);
}
{
const char *gid_origin;
int ret;
bool inbox;
"Maildir doesn't support streamed mailboxes");
return -1;
}
/* begin by checking if tmp/ directory exists and if it should be
cleaned up. */
if (ret > 0) {
/* exists */
return maildir_mailbox_open_existing(box);
}
if (ret < 0)
return -1;
/* tmp/ directory doesn't exist. does the maildir? */
/* yes, we'll need to create the missing dirs */
return -1;
return maildir_mailbox_open_existing(box);
return -1;
} else {
return -1;
}
}
static int
const char *gid_origin)
{
const char *path;
int fd;
/* add the execute bit if either read or write bit is set */
return -1;
if (fd == -1) {
return -1;
}
gid, gid_origin));
} else {
"fchown(%s) failed: %m", path);
}
}
return 0;
}
static int
{
int ret;
if (maildir_uidlist_lock(uidlist) <= 0)
return -1;
if (update->uid_validity != 0)
if (update->min_next_uid != 0) {
FALSE);
}
/* FIXME: update highestmodseq */
return ret;
}
static int
bool directory)
{
int fd;
/* if dovecot-shared exists in the root dir, create the mailbox using
its permissions and gid, and copy the dovecot-shared inside it. */
gid_origin) < 0)
return -1;
} else {
FALSE) < 0)
return -1;
}
/* Maildir++ spec wants that maildirfolder named file is created for
all subfolders. */
if (fd != -1) {
/* if dovecot-shared exists, use the same group */
/* doesn't exist */
/* ok */
} else {
"fchown(%s) failed: %m", path);
}
"Mailbox was deleted while it was being created");
return -1;
} else {
"open(%s, O_CREAT) failed: %m", path);
}
}
static void
struct mailbox_status *status_r)
{
if ((items & STATUS_GUID) != 0) {
}
}
static const char *
{
const char *root_dir;
char sep;
return NULL;
MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) != 0) {
/* Not maildir++ driver. Don't use this trick. */
return NULL;
}
}
static int
const char *name)
{
struct dirent *d;
unsigned int dir_len;
bool unlinked_something = FALSE;
} else {
"opendir(%s) failed: %m", path);
}
return -1;
}
errno = 0;
if (d->d_name[0] == '.') {
/* skip . and .. */
continue;
continue;
}
if (maildir_is_internal_name(d->d_name)) {
"unlink_directory(%s) failed: %m",
} else {
}
continue;
}
/* trying to unlink() a directory gives either EPERM or EISDIR
(non-POSIX). it doesn't really work anywhere in practise,
so don't bother stat()ing the file first */
"unlink_directory(%s) failed: %m",
}
}
path);
}
return -1;
}
if (!unlinked_something) {
t_strdup_printf("Directory %s isn't empty, "
"can't delete it.", name));
return -1;
}
return 0;
}
static int
{
int count;
/* Make sure the indexes are closed before trying to delete the
directory that contains them. It can still fail with some NFS
implementations if indexes are opened by another session, but
that can't really be helped. */
/* delete the index and control directories */
return -1;
/* check if the mailbox actually exists */
return -1;
}
/* a symlink most likely */
"unlink(%s) failed: %m", src);
return -1;
}
return 0;
}
/* we shouldn't get this far if this is the actual INBOX.
be anyway sure that we don't accidentally delete the entire
maildir (INBOX explicitly configured to maildir root). */
"INBOX can't be deleted.");
return -1;
}
}
/* delete the directory directly without any renaming */
}
/* rename the .maildir into ..DOVECOT-TRASH which atomically
marks it as being deleted. If we die before deleting the
..DOVECOT-TRASH directory, it gets deleted the next time
mailbox listing sees it. */
count = 0;
/* it was just deleted under us by
another process */
return -1;
}
if (!EDESTDIREXISTS(errno)) {
return -1;
}
/* already existed, delete it and try again */
"unlink_directory(%s) failed: %m", dest);
return -1;
}
count++;
}
"unlink_directory(%s) failed: %m", dest);
/* it's already renamed to ..dir, which means it's
deleted as far as the client is concerned. Report
success. */
}
return 0;
}
static int
bool rename_children)
{
/* INBOX often exists as the root ~/Maildir.
We can't rename it then. */
"Renaming INBOX isn't supported.");
return -1;
}
}
}
{
}
}
{
else {
}
}
static int
const char *mailbox_name ATTR_UNUSED,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags)
{
if (maildir_is_internal_name(fname)) {
*flags |= MAILBOX_NONEXISTENT;
return 0;
}
switch (type) {
/* non-directories are not */
*flags |= MAILBOX_NOSELECT;
return 0;
break;
}
/* temporary NFS file */
*flags |= MAILBOX_NONEXISTENT;
} else {
*flags |= MAILBOX_NOSELECT |
}
return 0;
}
/* doesn't exist - probably a non-existing subscribed mailbox */
*flags |= MAILBOX_NONEXISTENT;
} else {
/* non-selectable. probably either access denied, or symlink
destination not found. don't bother logging errors. */
*flags |= MAILBOX_NOSELECT;
}
/* make sure it's a selectable mailbox */
*flags |= MAILBOX_NOSELECT;
/* now we can figure out based on the link count if we
have child mailboxes or not. for a selectable
mailbox we have 3 more links (cur/, new/ and tmp/)
than non-selectable. */
if ((*flags & MAILBOX_NOSELECT) == 0) {
*flags |= MAILBOX_CHILDREN;
else
*flags |= MAILBOX_NOCHILDREN;
} else {
*flags |= MAILBOX_CHILDREN;
else
*flags |= MAILBOX_NOCHILDREN;
}
} else {
/* link count 3 may mean either a selectable mailbox
or a non-selectable mailbox with 1 child. */
*flags |= MAILBOX_CHILDREN;
if ((*flags & MAILBOX_NOSELECT) != 0)
*flags |= MAILBOX_CHILDREN;
else
*flags |= MAILBOX_NOCHILDREN;
}
}
}
return 1;
}
static int
const char *mailbox_name ATTR_UNUSED,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags)
{
int ret;
const char *path;
/* this directory is in the middle of being deleted,
or the process trying to delete it had died.
delete it ourself if it's been there longer than
one hour. */
*flags |= MAILBOX_NONEXISTENT;
return 0;
}
switch (type) {
/* all directories are valid maildirs */
return 1;
/* non-directories are not */
*flags |= MAILBOX_NOSELECT;
return 0;
/* need to check with stat() to be sure */
break;
}
/* Check files beginning with .nfs always because they may be
temporary files created by the kernel */
const char *path;
/* if fname="", we're checking if a base maildir has INBOX */
ret = 1;
else {
*flags |= MAILBOX_NONEXISTENT;
else
*flags |= MAILBOX_NOSELECT;
ret = 0;
}
/* just deleted? */
*flags |= MAILBOX_NONEXISTENT;
ret = 0;
} else {
*flags |= MAILBOX_NOSELECT;
ret = 0;
}
} else {
ret = 1;
}
return ret;
}
{
const char *path;
return mailbox_uidvalidity_next(path);
}
static void maildir_class_init(void)
{
}
static void maildir_class_deinit(void)
{
}
struct mailbox_list *list)
{
struct maildir_mailbox_list *mlist;
} else {
list->v.is_valid_create_name =
}
}
struct mail_storage maildir_storage = {
MEMBER(class_flags) 0,
{
}
};
struct mailbox maildir_mailbox = {
{
NULL,
NULL,
NULL,
NULL,
}
};