Skip to content

Commit

Permalink
Merge pull request #5002 from brong/rename-ab
Browse files Browse the repository at this point in the history
FastMail: test that rename a->c, b->a, c->b works
  • Loading branch information
brong committed Aug 21, 2024
2 parents 07098f5 + 1cdad7a commit 4874b88
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 4 deletions.
40 changes: 40 additions & 0 deletions cassandane/tiny-tests/FastMail/mailbox_rename_long_chain
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!perl
use Cassandane::Tiny;

sub test_mailbox_rename_long_chain
:AllowMoves :Replication :SyncLog :min_version_3_3
:needs_component_replication
{
my ($self) = @_;

my $mtalk = $self->{master_store}->get_client();

$mtalk->create('INBOX.Foo');
$mtalk->create('INBOX.Bar');

# replicate and check initial state
my $synclogfname = "$self->{instance}->{basedir}/conf/sync/log";
$self->run_replication(rolling => 1, inputfile => $synclogfname);
unlink($synclogfname);
$self->check_replication('cassandane');

# perform the multi-path rename
$mtalk = $self->{master_store}->get_client();
$mtalk->rename('INBOX.Bar', 'Inbox.Old');
$mtalk->rename('INBOX.Foo', 'Inbox.Foo2');
$mtalk->rename('INBOX.Foo2', 'Inbox.Foo3');
$mtalk->rename('INBOX.Foo3', 'Inbox.Foo4');
$mtalk->rename('INBOX.Foo4', 'Inbox.Foo5');
$mtalk->rename('INBOX.Foo5', 'Inbox.Foo6');
$mtalk->rename('INBOX.Foo6', 'Inbox.Foo7');
$mtalk->rename('INBOX.Foo7', 'Inbox.Foo8');
$mtalk->rename('INBOX.Foo8', 'Inbox.Bar');
# Create a couple of intermediates again
$mtalk->create('INBOX.Foo5');
$mtalk->create('INBOX.Foo2');

# replicate and check that it syncs ok
$self->run_replication(rolling => 1, inputfile => $synclogfname);
unlink($synclogfname);
$self->check_replication('cassandane');
}
31 changes: 31 additions & 0 deletions cassandane/tiny-tests/FastMail/mailbox_rename_switch_ab
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!perl
use Cassandane::Tiny;

sub test_mailbox_rename_switch_ab
:AllowMoves :Replication :SyncLog :min_version_3_3
:needs_component_replication
{
my ($self) = @_;

my $mtalk = $self->{master_store}->get_client();

$mtalk->create('INBOX.Foo');
$mtalk->create('INBOX.Bar');

# replicate and check initial state
my $synclogfname = "$self->{instance}->{basedir}/conf/sync/log";
$self->run_replication(rolling => 1, inputfile => $synclogfname);
unlink($synclogfname);
$self->check_replication('cassandane');

# perform the three way rename
$mtalk = $self->{master_store}->get_client();
$mtalk->rename('INBOX.Foo', 'Inbox.Foo2');
$mtalk->rename('INBOX.Bar', 'Inbox.Foo');
$mtalk->rename('INBOX.Foo2', 'Inbox.Bar');

# replicate and check that it syncs ok
$self->run_replication(rolling => 1, inputfile => $synclogfname);
unlink($synclogfname);
$self->check_replication('cassandane');
}
8 changes: 5 additions & 3 deletions imap/mailbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -2675,19 +2675,21 @@ EXPORTED void mailbox_unlock_index(struct mailbox *mailbox, struct statusdata *s
abort();
}

// we always write if given new statusdata, or if we changed the mailbox
int write_status = !!sdata;
if (mailbox->has_changed) {
sync_log_mailbox(mailbox_name(mailbox));

if (!sdata) {
if (!sdata && !(mailbox->i.options & OPT_MAILBOX_DELETED) && !strcmpsafe(mailbox_name(mailbox), mailbox->h.name)) {
status_fill_mailbox(mailbox, &mysdata);
sdata = &mysdata;
}

mailbox->has_changed = 0;
write_status = 1;
}

// we always write if given new statusdata, or if we changed the mailbox
if (sdata)
if (write_status)
statuscache_invalidate(mailbox_name(mailbox), sdata);

if (mailbox->index_locktype) {
Expand Down
40 changes: 39 additions & 1 deletion imap/sync_support.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,18 @@ struct sync_folder *sync_folder_lookup(struct sync_folder_list *l,
return NULL;
}

const struct sync_folder *sync_folder_lookup_byname(struct sync_folder_list *l,
const char *name)
{
struct sync_folder *p;

for (p = l->head; p; p = p->next) {
if (!strcmp(p->name, name))
return p;
}
return NULL;
}

void sync_folder_list_free(struct sync_folder_list **lp)
{
struct sync_folder_list *l = *lp;
Expand Down Expand Up @@ -6831,6 +6843,32 @@ static int do_folders(struct sync_client_state *sync_cs,
* short and contain few dependancies. Algorithm is to simply pick a
* rename operation which has no dependancy and repeat until done */

struct sync_rename *item;
for (item = rename_folders->head; item; item = item->next) {
if (!strcmp(item->oldname, item->newname)) continue;
if (!sync_folder_lookup_byname(replica_folders, item->oldname)) continue;
if (!sync_folder_lookup_byname(replica_folders, item->newname)) continue;

// ok, it's a rename and both source and destination names exist already,
// is there an intermediate we can rename to?

mbentry_t *mbentry_byid = NULL;
if (mboxlist_lookup_by_uniqueid(item->uniqueid, &mbentry_byid, NULL)) continue;
int i;
for (i = 0; i < ptrarray_size(&mbentry_byid->name_history); i++) {
const former_name_t *histitem = ptrarray_nth(&mbentry_byid->name_history, i);
if (sync_folder_lookup_byname(replica_folders, histitem->name)) continue;
// add a rename from old name to the temporary name
sync_rename_list_add(rename_folders, item->uniqueid, item->oldname,
histitem->name, item->part, histitem->uidvalidity);
// and then reuse this item for the rename from temporary to final
free(item->oldname);
item->oldname = xstrdup(histitem->name);
break;
}
mboxlist_entry_free(&mbentry_byid);
}

while (rename_folders->done < rename_folders->count) {
int rename_success = 0;
struct sync_rename *item, *item2 = NULL;
Expand All @@ -6841,7 +6879,7 @@ static int do_folders(struct sync_client_state *sync_cs,
/* don't skip rename to different partition */
if (strcmp(item->oldname, item->newname)) {
item2 = sync_rename_lookup(rename_folders, item->newname);
if (item2 && !item2->done) continue;
if (item2 && !item2->done && sync_folder_lookup_byname(replica_folders, item->newname)) continue;
}

/* Found unprocessed item which should rename cleanly */
Expand Down

0 comments on commit 4874b88

Please sign in to comment.