예제 #1
0
static void mailbox_list_try_delete(struct mailbox_list *list, const char *name,
				    enum mailbox_list_path_type type)
{
	const char *mailbox_path, *path;

	mailbox_path = mailbox_list_get_path(list, name,
					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
	path = mailbox_list_get_path(list, name, type);
	if (path == NULL || *path == '\0' || strcmp(path, mailbox_path) == 0)
		return;

	if (*list->set.maildir_name == '\0' &&
	    (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) {
		/* this directory may contain also child mailboxes' data.
		   we don't want to delete that. */
		bool rmdir_path = *list->set.maildir_name != '\0';
		if (mailbox_list_delete_mailbox_nonrecursive(list, name, path,
							     rmdir_path) < 0)
			return;
	} else {
		if (mailbox_list_delete_trash(path) < 0 &&
		    errno != ENOENT && errno != ENOTEMPTY) {
			mailbox_list_set_critical(list,
				"unlink_directory(%s) failed: %m", path);
		}
	}

	/* avoid leaving empty directories lying around */
	mailbox_list_delete_until_root(list, path, type);
}
예제 #2
0
static int
mbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
		    const char **error_r)
{
	struct mbox_storage *storage = (struct mbox_storage *)_storage;
	struct stat st;
	const char *dir;

	if (master_service_get_client_limit(master_service) > 1) {
		/* we can't handle locking related problems. */
		*error_r = "mbox requires client_limit=1 for service";
		return -1;
	}

	storage->set = mail_storage_get_driver_settings(_storage);

	dir = mailbox_list_get_path(ns->list, NULL,
				    MAILBOX_LIST_PATH_TYPE_INDEX);
	if (*dir != '\0') {
		_storage->temp_path_prefix = p_strconcat(_storage->pool, dir,
			"/", mailbox_list_get_temp_prefix(ns->list), NULL);
	}
	if (stat(ns->list->set.root_dir, &st) == 0 && !S_ISDIR(st.st_mode)) {
		*error_r = t_strdup_printf(
			"mbox root directory can't be a file: %s "
			"(http://wiki.dovecot.org/MailLocation/Mbox)",
			ns->list->set.root_dir);
		return -1;
	}
	return 0;
}
예제 #3
0
static int mbox_mailbox_open_existing(struct mbox_mailbox *mbox)
{
	struct mailbox *box = &mbox->box;
	const char *rootdir;
	bool move_to_memory;

	if (access(box->path, R_OK|W_OK) < 0) {
		if (errno != EACCES) {
			mbox_set_syscall_error(mbox, "access()");
			return -1;
		}
		mbox->box.backend_readonly = TRUE;
	}
	move_to_memory = want_memory_indexes(mbox->storage, box->path);

	if (box->inbox_any || strcmp(box->name, "INBOX") == 0) {
		/* if INBOX isn't under the root directory, it's probably in
		   /var/mail and we want to allow privileged dotlocking */
		rootdir = mailbox_list_get_path(box->list, NULL,
						MAILBOX_LIST_PATH_TYPE_DIR);
		if (strncmp(box->path, rootdir, strlen(rootdir)) != 0)
			mbox->mbox_privileged_locking = TRUE;
	}
	if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) {
		if (mbox_lock(mbox, F_WRLCK, &mbox->mbox_global_lock_id) <= 0)
			return -1;

		if (mbox->mbox_dotlock != NULL) {
			mbox->keep_lock_to =
				timeout_add(MBOX_LOCK_TOUCH_MSECS,
					    mbox_lock_touch_timeout, mbox);
		}
	}
	return index_storage_mailbox_open(box, move_to_memory);
}
예제 #4
0
static int create_inbox(struct mailbox *box)
{
	const char *inbox_path;
	int fd;

	inbox_path = mailbox_list_get_path(box->list, "INBOX",
					   MAILBOX_LIST_PATH_TYPE_MAILBOX);

	fd = open(inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660);
	if (fd == -1 && errno == EACCES) {
		/* try again with increased privileges */
		(void)restrict_access_use_priv_gid();
		fd = open(inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660);
		restrict_access_drop_priv_gid();
	}
	if (fd != -1) {
		(void)close(fd);
		return 0;
	} else if (errno == EACCES) {
		mail_storage_set_critical(box->storage, "%s",
			mail_error_create_eacces_msg("open", inbox_path));
		return -1;
	} else if (errno == EEXIST) {
		mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
				       "Mailbox already exists");
		return -1;
	} else {
		mail_storage_set_critical(box->storage,
			"open(%s, O_CREAT) failed: %m", inbox_path);
		return -1;
	}
}
예제 #5
0
int mailbox_list_delete_symlink_default(struct mailbox_list *list,
					const char *name)
{
	const char *path;
	int ret;

	ret = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
				    &path);
	if (ret < 0)
		return -1;
	i_assert(ret > 0);

	if (unlink(path) == 0)
		return 0;

	if (errno == ENOENT) {
		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
			T_MAILBOX_LIST_ERR_NOT_FOUND(list, name));
	} else if (errno == EISDIR ||
		   errno == EPERM) { /* Solaris */
		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
				       "Mailbox isn't a symlink");
	} else {
		mailbox_list_set_critical(list, "unlink(%s) failed: %m", path);
	}
	return -1;
}
예제 #6
0
static const char *cydir_mail_get_path(struct mail *mail)
{
	const char *dir;

	dir = mailbox_list_get_path(mail->box->list, mail->box->name,
				    MAILBOX_LIST_PATH_TYPE_MAILBOX);
	return t_strdup_printf("%s/%u.", dir, mail->uid);
}
static const char *
acl_backend_vfile_get_local_dir(struct acl_backend *backend,
				const char *name, const char *vname)
{
	struct mail_namespace *ns = mailbox_list_get_namespace(backend->list);
	struct mailbox_list *list = ns->list;
	struct mail_storage *storage;
	enum mailbox_list_path_type type;
	const char *dir, *inbox;

	if (*name == '\0')
		name = NULL;

	/* ACL files are very important. try to keep them among the main
	   mail files. that's not possible though with a) if the mailbox is
	   a file or b) if the mailbox path doesn't point to filesystem. */
	if (mailbox_list_get_storage(&list, vname, &storage) < 0)
		return NULL;
	i_assert(list == ns->list);

	type = mail_storage_is_mailbox_file(storage) ||
		(storage->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0 ?
		MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_MAILBOX;
	if (name == NULL) {
		if (!mailbox_list_get_root_path(list, type, &dir))
			return NULL;
	} else {
		if (mailbox_list_get_path(list, name, type, &dir) <= 0)
			return NULL;
	}

	/* verify that the directory isn't same as INBOX's directory.
	   this is mainly for Maildir. */
	if (name == NULL &&
	    mailbox_list_get_path(list, "INBOX",
				  MAILBOX_LIST_PATH_TYPE_MAILBOX, &inbox) > 0 &&
	    strcmp(inbox, dir) == 0) {
		/* can't have default ACLs with this setup */
		return NULL;
	}
	return dir;
}
static bool
acl_backend_vfile_has_acl(struct acl_backend *_backend, const char *name)
{
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_backend;
	struct acl_backend_vfile_validity *old_validity, new_validity;
	const char *path, *local_path, *global_path, *dir, *vname = "";
	const char *error;
	int ret;

	old_validity = acl_cache_get_validity(_backend->cache, name);
	if (old_validity != NULL)
		new_validity = *old_validity;
	else
		memset(&new_validity, 0, sizeof(new_validity));

	/* 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 (mailbox_list_get_path(_backend->list, name,
				  MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) <= 0)
		ret = -1;
	else {
		ret = acl_backend_vfile_exists(backend, path,
					       &new_validity.mailbox_validity);
	}

	if (ret == 0 &&
	    (*name == '\0' ||
	     mailbox_list_is_valid_name(_backend->list, name, &error))) {
		vname = *name == '\0' ? "" :
			mailbox_list_get_vname(_backend->list, name);
		dir = acl_backend_vfile_get_local_dir(_backend, name, vname);
		if (dir != NULL) {
			local_path = t_strconcat(dir, "/", name, NULL);
			ret = acl_backend_vfile_exists(backend, local_path,
						       &new_validity.local_validity);
		}
	}

	if (ret == 0 && backend->global_path != NULL) {
		if (_backend->global_file != NULL) {
			ret = acl_global_file_refresh(_backend->global_file);
			if (ret == 0 && acl_global_file_have_any(_backend->global_file, vname))
				ret = 1;
		} else {
			global_path = t_strconcat(backend->global_path, "/", name, NULL);
			ret = acl_backend_vfile_exists(backend, global_path,
						       &new_validity.global_validity);
		}
	}
	acl_cache_set_validity(_backend->cache, name, &new_validity);
	return ret > 0;
}
예제 #9
0
static int rename_dir(struct mailbox_list *oldlist, const char *oldname,
		      struct mailbox_list *newlist, const char *newname,
		      enum mailbox_list_path_type type)
{
	const char *oldpath, *newpath;

	if (mailbox_list_get_path(oldlist, oldname, type, &oldpath) <= 0 ||
	    mailbox_list_get_path(newlist, newname, type, &newpath) <= 0)
		return 0;

	if (strcmp(oldpath, newpath) == 0)
		return 0;

	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
		mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
					  oldpath, newpath);
		return -1;
	}
	return 0;
}
예제 #10
0
static bool
is_inbox_file(struct mailbox_list *list, const char *path, const char *fname)
{
	const char *inbox_path;

	if (strcasecmp(fname, "INBOX") != 0)
		return FALSE;

	if (mailbox_list_get_path(list, "INBOX",
				  MAILBOX_LIST_PATH_TYPE_MAILBOX,
				  &inbox_path) <= 0)
		i_unreached();
	return strcmp(inbox_path, path) == 0;
}
예제 #11
0
static const char *acl_list_get_root_dir(struct acl_backend_vfile *backend)
{
	struct mail_storage *storage;
	const char *rootdir, *maildir;

	rootdir = mailbox_list_get_path(backend->backend.list, NULL,
					MAILBOX_LIST_PATH_TYPE_DIR);

	storage = mailbox_list_get_namespace(backend->backend.list)->storage;
	if (mail_storage_is_mailbox_file(storage)) {
		maildir = mailbox_list_get_path(backend->backend.list, NULL,
						MAILBOX_LIST_PATH_TYPE_MAILBOX);
		if (strcmp(maildir, rootdir) == 0) {
			/* dovecot-acl-list would show up as a mailbox if we
			   created it to root dir. since we don't really have
			   any other good alternatives, place it to control
			   dir */
			rootdir = mailbox_list_get_path(backend->backend.list,
					NULL, MAILBOX_LIST_PATH_TYPE_CONTROL);
		}
	}
	return rootdir;
}
예제 #12
0
static int
imapc_list_get_path(struct mailbox_list *_list, const char *name,
		    enum mailbox_list_path_type type, const char **path_r)
{
	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
	struct mailbox_list *fs_list = imapc_list_get_fs(list);
	const char *fs_name;

	if (fs_list != NULL) {
		fs_name = imapc_list_get_fs_name(list, name);
		return mailbox_list_get_path(fs_list, fs_name, type, path_r);
	} else {
		*path_r = NULL;
		return 0;
	}
}
예제 #13
0
static int
mailbox_list_check_root_delete(struct mailbox_list *list, const char *name,
			       const char *path)
{
	const char *root_dir;

	root_dir = mailbox_list_get_path(list, NULL,
					 MAILBOX_LIST_PATH_TYPE_DIR);
	if (strcmp(root_dir, path) != 0)
		return 0;

	if (strcmp(name, "INBOX") == 0 &&
	    (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
				       "INBOX can't be deleted.");
		return -1;
	}
	mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
			       "Mail storage root can't be deleted.");
	return -1;
}
예제 #14
0
static int maildir_list_delete_dir(struct mailbox_list *list, const char *name)
{
	const char *path;
	struct stat st;

	/* with maildir++ there aren't any non-selectable mailboxes.
	   we'll always fail. */
	if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
				  &path) <= 0)
		i_unreached();
	if (stat(path, &st) == 0) {
		mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
				       "Mailbox exists");
	} else if (errno == ENOENT || errno == ENOTDIR) {
		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
	} else {
		mailbox_list_set_critical(list, "stat(%s) failed: %m", path);
	}
	return -1;
}
예제 #15
0
void mailbox_list_delete_until_root(struct mailbox_list *list, const char *path,
				    enum mailbox_list_path_type type)
{
	const char *root_dir, *p;
	unsigned int len;

	root_dir = mailbox_list_get_path(list, NULL, type);
	if (strncmp(path, root_dir, strlen(root_dir)) != 0) {
		/* mbox workaround: name=child/box, root_dir=mail/.imap/,
		   path=mail/child/.imap/box. we'll want to try to delete
		   the .imap/ part, but no further. */
		len = strlen(path);
		while (len > 0 && path[len-1] != '/')
			len--;
		if (len == 0)
			return;
		len--;
		while (len > 0 && path[len-1] != '/')
			len--;
		if (len == 0)
			return;

		root_dir = t_strndup(path, len-1);
	}
	while (strcmp(path, root_dir) != 0) {
		if (rmdir(path) < 0 && errno != ENOENT) {
			if (errno == ENOTEMPTY || errno == EEXIST)
				return;

			mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
						  path);
			return;
		}
		p = strrchr(path, '/');
		if (p == NULL)
			break;

		path = t_strdup_until(path, p);
	}
}
예제 #16
0
static int
maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
{
	const char *path;
	int ret;

	if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) {
		ret = mailbox_list_get_path(list, name,
					    MAILBOX_LIST_PATH_TYPE_MAILBOX,
					    &path);
		if (ret < 0)
			return -1;
		i_assert(ret > 0);
		ret = mailbox_list_delete_mailbox_file(list, name, path);
	} else {
		ret = maildir_list_delete_maildir(list, name);
	}

	if (ret == 0 || (list->props & MAILBOX_LIST_PROP_AUTOCREATE_DIRS) != 0)
		mailbox_list_delete_finish(list, name);
	return ret;
}
예제 #17
0
int mailbox_list_delete_mailbox_file(struct mailbox_list *list,
				     const char *name)
{
	const char *path;

	path = mailbox_list_get_path(list, name,
				     MAILBOX_LIST_PATH_TYPE_MAILBOX);

	/* we can simply unlink() the file */
	if (unlink(path) == 0)
		return 0;
	else if (ENOTFOUND(errno)) {
		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
				       T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
		return -1;
	} else {
		if (!mailbox_list_set_error_from_errno(list)) {
			mailbox_list_set_critical(list,
				"unlink(%s) failed: %m", path);
		}
		return -1;
	}
}
예제 #18
0
static int
maildir_list_delete_maildir(struct mailbox_list *list, const char *name)
{
	const char *path, *trash_dir;
	int ret = 0;

	trash_dir = mailbox_list_maildir_get_trash_dir(list);
	ret = mailbox_list_delete_maildir_via_trash(list, name, trash_dir);
	if (ret < 0)
		return -1;

	if (ret == 0) {
		/* we could actually use just unlink_directory()
		   but error handling is easier this way :) */
		if (mailbox_list_get_path(list, name,
					  MAILBOX_LIST_PATH_TYPE_MAILBOX,
					  &path) <= 0)
			i_unreached();
		if (mailbox_list_delete_mailbox_nonrecursive(list, name,
							     path, TRUE) < 0)
			return -1;
	}
	return 0;
}
예제 #19
0
int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
					  const char *name,
					  const char *trash_dir)
{
	const char *src, *trash_dest;
	unsigned int count;

	src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX);
	if (mailbox_list_check_root_delete(list, name, src) < 0)
		return -1;

	/* rename the mailbox dir to trash dir, which atomically
	   marks it as being deleted. */
	count = 0; trash_dest = trash_dir;
	for (; rename(src, trash_dest) < 0; count++) {
		if (ENOTFOUND(errno)) {
			if (trash_dest != trash_dir && count < 5) {
				/* either the source was just deleted or
				   the trash dir was deleted. */
				trash_dest = trash_dir;
				continue;
			}
			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
			return -1;
		}
		if (errno == EXDEV) {
			/* can't do this the fast way */
			return 0;
		}
		if (!EDESTDIREXISTS(errno)) {
			if (mailbox_list_set_error_from_errno(list))
				return -1;
			mailbox_list_set_critical(list,
				"rename(%s, %s) failed: %m", src, trash_dest);
			return -1;
		}

		/* trash dir already exists. the reasons for this are:

		   a) another process is in the middle of deleting it
		   b) previous process crashed and didn't delete it
		   c) NFS: mailbox was recently deleted, but some connection
		      still has that mailbox open. the directory contains .nfs*
		      files that can't be deleted until the mailbox is closed.

		   Because of c) we'll first try to rename the mailbox under
		   the trash directory and only later try to delete the entire
		   trash directory. */
		if (trash_dir == trash_dest) {
			trash_dest = t_strconcat(trash_dir, "/",
						 unique_fname(), NULL);
		} else if (mailbox_list_delete_trash(trash_dest) < 0 &&
			   (errno != ENOTEMPTY || count >= 5)) {
			mailbox_list_set_critical(list,
				"unlink_directory(%s) failed: %m", trash_dest);
			return -1;
		}
	}

	if (mailbox_list_delete_trash(trash_dir) < 0 &&
	    errno != ENOTEMPTY && errno != EBUSY) {
		mailbox_list_set_critical(list,
			"unlink_directory(%s) failed: %m", trash_dir);

		/* it's already renamed to trash dir, which means it's
		   deleted as far as the client is concerned. Report
		   success. */
	}
	return 1;
}
예제 #20
0
static int
maildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
			struct mailbox_list *newlist, const char *newname)
{
	struct mailbox_list_iterate_context *iter;
        const struct mailbox_info *info;
	ARRAY(const char *) names_arr;
	const char *pattern, *oldpath, *newpath, *old_childname, *new_childname;
	const char *const *names, *old_vname, *new_vname;
	unsigned int i, count, old_vnamelen;
	pool_t pool;
	char old_ns_sep;
	int ret;

	ret = 0;

	/* first get the list of the children and save them to memory, because
	   we can't rely on readdir() not skipping files while the directory
	   is being modified. this doesn't protect against modifications by
	   other processes though. */
	pool = pool_alloconly_create("Maildir++ children list", 1024);
	i_array_init(&names_arr, 64);

	old_vname = mailbox_list_get_vname(oldlist, oldname);
	old_vnamelen = strlen(old_vname);

	new_vname = mailbox_list_get_vname(newlist, newname);

	old_ns_sep = mail_namespace_get_sep(oldlist->ns);
	pattern = t_strdup_printf("%s%c*", old_vname, old_ns_sep);
	iter = mailbox_list_iter_init(oldlist, pattern,
				      MAILBOX_LIST_ITER_RETURN_NO_FLAGS |
				      MAILBOX_LIST_ITER_RAW_LIST);
	while ((info = mailbox_list_iter_next(iter)) != NULL) {
		const char *name;

		/* verify that the prefix matches, otherwise we could have
		   problems with mailbox names containing '%' and '*' chars */
		if (strncmp(info->vname, old_vname, old_vnamelen) == 0 &&
		    info->vname[old_vnamelen] == old_ns_sep) {
			name = p_strdup(pool, info->vname + old_vnamelen);
			array_append(&names_arr, &name, 1);
		}
	}
	if (mailbox_list_iter_deinit(&iter) < 0) {
		ret = -1;
		names = NULL; count = 0;
	} else {
		names = array_get(&names_arr, &count);
	}

	for (i = 0; i < count; i++) {
		old_childname = mailbox_list_get_storage_name(oldlist,
					t_strconcat(old_vname, names[i], NULL));
		if (strcmp(old_childname, new_vname) == 0) {
			/* When doing RENAME "a" "a.b" we see "a.b" here.
			   We don't want to rename it anymore to "a.b.b". */
			continue;
		}

		new_childname = mailbox_list_get_storage_name(newlist,
					t_strconcat(new_vname, names[i], NULL));
		if (mailbox_list_get_path(oldlist, old_childname,
					  MAILBOX_LIST_PATH_TYPE_MAILBOX,
					  &oldpath) <= 0 ||
		    mailbox_list_get_path(newlist, new_childname,
					  MAILBOX_LIST_PATH_TYPE_MAILBOX,
					  &newpath) <= 0)
			i_unreached();

		/* FIXME: it's possible to merge two mailboxes if either one of
		   them doesn't have existing root mailbox. We could check this
		   but I'm not sure if it's worth it. It could be even
		   considered as a feature.

		   Anyway, the bug with merging is that if both mailboxes have
		   identically named child mailbox they conflict. Just ignore
		   those and leave them under the old mailbox. */
		if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
			ret = 1;
		else {
			mailbox_list_set_critical(oldlist,
				"rename(%s, %s) failed: %m", oldpath, newpath);
			ret = -1;
			break;
		}

		(void)rename_dir(oldlist, old_childname, newlist, new_childname,
				 MAILBOX_LIST_PATH_TYPE_CONTROL);
		(void)rename_dir(oldlist, old_childname, newlist, new_childname,
				 MAILBOX_LIST_PATH_TYPE_INDEX);
	}
	array_free(&names_arr);
	pool_unref(&pool);

	return ret;
}
예제 #21
0
static int
maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
			    struct mailbox_list *newlist, const char *newname)
{
	const char *oldpath, *newpath, *root_path;
	int ret;
        bool found;

	/* NOTE: it's possible to rename a nonexistent mailbox which has
	   children. In that case we should ignore the rename() error. */
	if (mailbox_list_get_path(oldlist, oldname,
				  MAILBOX_LIST_PATH_TYPE_MAILBOX, &oldpath) <= 0 ||
	    mailbox_list_get_path(newlist, newname,
				  MAILBOX_LIST_PATH_TYPE_MAILBOX, &newpath) <= 0)
		i_unreached();

	root_path = mailbox_list_get_root_forced(oldlist,
						 MAILBOX_LIST_PATH_TYPE_MAILBOX);
	if (strcmp(oldpath, root_path) == 0) {
		/* most likely INBOX */
		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
			t_strdup_printf("Renaming %s isn't supported.",
					oldname));
		return -1;
	}

	/* if we're renaming under another mailbox, require its permissions
	   to be same as ours. */
	if (strchr(newname, mailbox_list_get_hierarchy_sep(newlist)) != NULL) {
		struct mailbox_permissions old_perm, new_perm;

		mailbox_list_get_permissions(oldlist, oldname, &old_perm);
		mailbox_list_get_permissions(newlist, newname, &new_perm);

		if ((new_perm.file_create_mode != old_perm.file_create_mode ||
		     new_perm.dir_create_mode != old_perm.dir_create_mode ||
		     new_perm.file_create_gid != old_perm.file_create_gid)) {
			mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
				"Renaming not supported across conflicting "
				"directory permissions");
			return -1;
		}
	}


	ret = rename(oldpath, newpath);
	if (ret == 0 || errno == ENOENT) {
		(void)rename_dir(oldlist, oldname, newlist, newname,
				 MAILBOX_LIST_PATH_TYPE_CONTROL);
		(void)rename_dir(oldlist, oldname, newlist, newname,
				 MAILBOX_LIST_PATH_TYPE_INDEX);

		found = ret == 0;
		T_BEGIN {
			ret = maildir_rename_children(oldlist, oldname,
						      newlist, newname);
		} T_END;
		if (ret < 0)
			return -1;
		if (!found && ret == 0) {
			mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
				T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname));
			return -1;
		}

		return 0;
	}