maildir-storage.c revision 8ebc5601a597208c14ea1071c3730bc6ec2ca4d0
/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "hostpid.h"
#include "str.h"
#include "home-expand.h"
#include "mkdir-parents.h"
#include "unlink-directory.h"
#include "unlink-old-files.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_PLUSPLUS_DRIVER_NAME "maildir++"
#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
#define MAILDIR_LIST_CONTEXT(obj) \
struct rename_context {
bool found;
const char *newname;
};
extern struct mail_storage maildir_storage;
extern struct mailbox maildir_mailbox;
static int
static int
static int
const char *mailbox_name,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags_r);
static int
const char *mailbox_name,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags_r);
static int
{
if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) {
*error_r = "Root mail directory not given";
return -1;
}
/* we'll need to figure out the maildir location ourself.
if (debug) {
i_info("maildir: root exists (%s)",
path);
}
} else {
if (debug) {
i_info("maildir: access(%s, rwx): "
"failed: %m", path);
}
}
} else {
if (debug)
i_info("maildir: HOME not set");
}
if (debug)
i_info("maildir: /cur exists, assuming chroot");
}
} else {
if (debug)
error_r) < 0)
return -1;
}
if (debug)
i_info("maildir: couldn't find root dir");
*error_r = "Root mail directory not given";
return -1;
}
return 0;
}
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_alloc(void)
{
struct maildir_storage *storage;
}
static int
const char **error_r)
{
struct mailbox_list_settings list_set;
struct mailbox_list *list;
const char *layout;
error_r) < 0)
return -1;
/* Maildir++ INBOX is the Maildir base itself */
}
if ((flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) != 0) {
/* ok */
return -1;
"Root mail directory doesn't exist: %s",
return -1;
} else {
return -1;
}
}
return -1;
} else {
list->v.is_valid_create_name =
}
/* finish list init after we've overridden vfuncs */
/* put the temp files into tmp/ directory preferrably */
}
return 0;
}
{
const char *path;
if (debug)
return FALSE;
}
if (debug)
return FALSE;
}
return TRUE;
}
{
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");
} 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 */
{
int ret;
if (ret > 0) {
if (!verify) {
"Mailbox already exists");
return -1;
}
return 1;
}
if (ret < 0)
return -1;
/* doesn't exist, create */
return -1;
return -1;
return -1;
return 0;
}
{
}
{
/* add the execute bit if either read or write bit is set */
return mode;
}
static struct mailbox *
enum mailbox_open_flags flags)
{
struct maildir_mailbox *mbox;
struct mail_index *index;
const char *path, *control_dir;
/* for shared mailboxes get the create mode from the
permissions of dovecot-shared file. */
/* Ignore GID */
}
}
sizeof(mbox->maildir_hdr), 0, 0);
if ((flags & MAILBOX_OPEN_KEEP_LOCKED) != 0) {
mailbox_close(&box);
return NULL;
}
mbox);
}
}
static struct mailbox *
{
const char *path;
int ret;
"Maildir doesn't support streamed mailboxes");
return NULL;
}
/* INBOX always exists */
return NULL;
}
/* begin by checking if tmp/ directory exists and if it should be
cleaned up. */
if (ret > 0) {
/* exists */
}
if (ret < 0)
return NULL;
/* tmp/ directory doesn't exist. does the maildir? */
/* yes, we'll need to create the missing dirs */
return NULL;
return NULL;
} else {
path);
return NULL;
}
}
{
const char *path;
unsigned int i;
int fd;
/* add the execute bit if either read or write bit is set */
return -1;
}
for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
"chown(%s) failed: %m", path);
}
}
if (fd == -1) {
return -1;
}
"fchown(%s) failed: %m", path);
}
return 0;
}
const char *name,
bool directory ATTR_UNUSED)
{
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. */
}
return -1;
/* Maildir++ spec want that maildirfolder named file is created for
all subfolders. */
if (fd != -1)
"Mailbox was deleted while it was being created");
return -1;
} else {
}
return 0;
}
static const char *
{
const char *root_dir;
char sep;
return NULL;
MAILDIR_PLUSPLUS_DRIVER_NAME) != 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;
}
{
/* INBOX often exists as the root ~/Maildir.
We can't rename it then. */
"Renaming INBOX isn't supported.");
return -1;
}
}
}
{
}
return index_storage_mailbox_close(box);
}
{
else {
}
}
static int
const char *mailbox_name ATTR_UNUSED,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags_r)
{
const char *path;
if (maildir_is_internal_name(fname)) {
return 0;
}
switch (type) {
/* non-directories are not */
return 0;
break;
}
return 1;
/* temporary NFS file */
return 0;
} else {
return 0;
}
/* this was a directory. maybe it has children. */
return 1;
} else {
return 0;
}
}
static int
const char *mailbox_name ATTR_UNUSED,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags_r)
{
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. */
return 0;
}
switch (type) {
/* all directories are valid maildirs */
return 1;
/* non-directories are not */
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 {
else
ret = 0;
}
/* just deleted? */
ret = 0;
} else {
ret = 0;
}
} else {
ret = 1;
}
return ret;
}
static void maildir_class_init(void)
{
}
static void maildir_class_deinit(void)
{
}
struct mail_storage maildir_storage = {
{
}
};
struct mailbox maildir_mailbox = {
{
NULL,
}
};