Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mailbox: no hierarchy walk when deleting uuid mailboxes #4764

Merged
merged 3 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions cassandane/Cassandane/Cyrus/Delete.pm
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ sub check_folder_not_ondisk
}
}

sub check_syslog
{
my ($self, $instance) = @_;

my $remove_empty_pat = qr/Remove of supposedly empty directory/;

$self->assert_null($instance->_check_syslog($remove_empty_pat));
}

sub test_self_inbox_imm
:ImmediateDelete :SemidelayedExpunge :NoAltNameSpace
{
Expand Down Expand Up @@ -218,6 +227,8 @@ sub test_self_inbox_imm
$self->check_folder_not_ondisk($subfolder);
$self->check_folder_not_ondisk($inbox, deleted => 1);
$self->check_folder_not_ondisk($subfolder, deleted => 1);

$self->check_syslog($self->{instance});
}

sub test_self_inbox_del
Expand Down Expand Up @@ -293,6 +304,8 @@ sub test_self_inbox_del
$self->check_folder_not_ondisk($subfolder);
$self->check_folder_not_ondisk($inbox, deleted => 1);
$self->check_folder_not_ondisk($subfolder, deleted => 1);

$self->check_syslog($self->{instance});
}

# old version of this test for builds without newer httpd features
Expand Down Expand Up @@ -370,6 +383,8 @@ sub test_admin_inbox_imm_legacy
$self->check_folder_not_ondisk($subfolder);
$self->check_folder_not_ondisk($inbox, deleted => 1);
$self->check_folder_not_ondisk($subfolder, deleted => 1);

$self->check_syslog($self->{instance});
}

sub test_admin_inbox_imm
Expand Down Expand Up @@ -509,6 +524,8 @@ EOF
}
$self->assert_not_file_test($data->{user}{sieve}, '-e');
$self->assert_not_file_test($data->{xapian}{t1}, '-e');

$self->check_syslog($self->{instance});
}

sub test_admin_inbox_del
Expand Down Expand Up @@ -590,6 +607,8 @@ sub test_admin_inbox_del
$self->check_folder_not_ondisk($subfolder);
$self->check_folder_not_ondisk($inbox, deleted => 1);
$self->check_folder_not_ondisk($subfolder, deleted => 1);

$self->check_syslog($self->{instance});
}

sub test_bz3781
Expand Down Expand Up @@ -699,6 +718,8 @@ sub test_cyr_expire_delete

# and not exist from mbpath either...
$self->assert_null($self->{instance}->folder_to_deleted_directories("user.cassandane.$subfoldername"));

$self->check_syslog($self->{instance});
}

sub test_allowdeleted
Expand Down Expand Up @@ -741,6 +762,8 @@ sub test_allowdeleted
$talk->select($result->[1][2]);
$self->assert_str_equals('ok', $talk->get_last_completion_response());
$self->assert_num_equals(1, $talk->get_response_code('exists'));

$self->check_syslog($self->{instance});
}

sub test_cyr_expire_delete_with_annotation
Expand Down Expand Up @@ -811,6 +834,8 @@ sub test_cyr_expire_delete_with_annotation
xlog $self, "Run cyr_expire -D now, with -a, skipping annotation.";
$self->{instance}->run_command({ cyrus => 1 }, 'cyr_expire', '-D' => '0', '-a' );
$self->assert_not_file_test($path, '-d');

$self->check_syslog($self->{instance});
}

# https://github.com/cyrusimap/cyrus-imapd/issues/2413
Expand Down Expand Up @@ -883,6 +908,8 @@ sub test_cyr_expire_dont_resurrect_convdb

# expect user does not have a conversations database
$self->assert_not_file_test($convdbfile, '-f');

$self->check_syslog($self->{instance});
}

sub test_no_delete_with_children
Expand All @@ -906,6 +933,8 @@ sub test_no_delete_with_children

$talk->delete($subfolder);
$self->assert_str_equals('no', $talk->get_last_completion_response());

$self->check_syslog($self->{instance});
}

sub test_cyr_expire_inherit_annot
Expand Down Expand Up @@ -940,6 +969,8 @@ sub test_cyr_expire_inherit_annot
$talk->unselect();
$talk->select($subfolder);
$self->assert_num_equals(0, $talk->get_response_code('exists'));

$self->check_syslog($self->{instance});
}

sub test_cyr_expire_noexpire
Expand Down Expand Up @@ -991,6 +1022,8 @@ sub test_cyr_expire_noexpire
$talk->unselect();
$talk->select($subfolder);
$self->assert_num_equals(0, $talk->get_response_code('exists'));

$self->check_syslog($self->{instance});
}

sub test_cyr_expire_delete_noexpire
Expand Down Expand Up @@ -1048,6 +1081,8 @@ sub test_cyr_expire_delete_noexpire
xlog $self, "Run cyr_expire";
$self->{instance}->run_command({ cyrus => 1 }, 'cyr_expire', '-D' => '1s' );
$self->assert(!-d "$path");

$self->check_syslog($self->{instance});
}

1;
13 changes: 11 additions & 2 deletions cassandane/Cassandane/Instance.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1501,9 +1501,18 @@ sub _check_sanity

sub _check_syslog
{
my ($self) = @_;
my ($self, $pattern) = @_;

if (defined $pattern) {
# pattern is optional but must be a regex if present
die "getsyslog: pattern is not a regular expression"
if lc ref($pattern) ne 'regexp';
}

my @errors = $self->getsyslog(qr/ERROR|TRACELOG|Unknown code ____/);
my @lines = $self->getsyslog();
my @errors = grep {
m/ERROR|TRACELOG|Unknown code ____/ || ($pattern && m/$pattern/)
} @lines;

@errors = grep { not m/DBERROR.*skipstamp/ } @errors;

Expand Down
39 changes: 33 additions & 6 deletions imap/mailbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -6085,7 +6085,7 @@ static void mailbox_delete_files(const char *path)
}
}

/* Callback for use by cmd_delete */
/* Callback for use by mailbox_delete_cleanup */
static int chkchildren(const mbentry_t *mbentry,
void *rock)
{
Expand Down Expand Up @@ -6412,7 +6412,11 @@ static struct meta_file meta_files[] = {
* try to create a mailbox while the delete is underway.
* VERY tight race condition exists right now... */
/* we need an exclusive namelock for this */
HIDDEN int mailbox_delete_cleanup(struct mailbox *mailbox, const char *part, const char *name, const char *uniqueid)
/* XXX uniqueid here must be NULL if this is a legacy mailbox */
HIDDEN int mailbox_delete_cleanup(struct mailbox *mailbox,
const char *part,
const char *name,
const char *uniqueid)
{
strarray_t paths = STRARRAY_INITIALIZER;
int i;
Expand All @@ -6432,7 +6436,7 @@ HIDDEN int mailbox_delete_cleanup(struct mailbox *mailbox, const char *part, con
ntail = nbuf + strlen(nbuf);

if (mailbox && object_storage_enabled){
mailbox_remove_files_from_object_storage (mailbox, 0) ;
mailbox_remove_files_from_object_storage(mailbox, 0);
}

/* XXX - double XXX - this is a really ugly function. It should be
Expand Down Expand Up @@ -6465,14 +6469,36 @@ HIDDEN int mailbox_delete_cleanup(struct mailbox *mailbox, const char *part, con
for (i = 0; i < paths.count; i++) {
char *path = paths.data[i]; /* need direct reference, because we're fiddling */
r = rmdir(path);
if (r && errno != ENOENT)
if (r && errno != ENOENT) {
syslog(LOG_NOTICE,
"Remove of supposedly empty directory %s failed: %m",
path);
p = strrchr(path, '/');
if (p) *p = '\0';
}

if (!uniqueid) {
p = strrchr(path, '/');
if (p) *p = '\0';
}
}

if (uniqueid) {
/* n.b. careful here -- legacy mailboxes will also have uniqueids,
* but we don't pass the uniqueid to this function for those, so
* uniqueid being non-NULL here means we're dealing with a UUID
* mailbox.
* UUID mailboxes have a flat on disk structure, so once we've
* removed the listed paths we're done, no hierarchy walk needed.
*/
goto done;
}

/* XXX For legacy mailboxes, this hierarchy walk might have been
* XXX intended to remove intermediate mailboxes after their
* XXX last child was removed. In which case, the hierarchy walk
* XXX might be obsolete for legacy mailboxes too, since we no
* XXX longer create intermediate mailboxes.
*/

/* Check if parent mailbox exists */
ntail = strrchr(nbuf, '.');
if (!ntail || strchr(ntail, '!')) {
Expand All @@ -6496,6 +6522,7 @@ HIDDEN int mailbox_delete_cleanup(struct mailbox *mailbox, const char *part, con
}
} while (r == IMAP_MAILBOX_NONEXISTENT);

done:
strarray_fini(&paths);

return 0;
Expand Down