fix bug in imapserver with rename of inbox, and add consistency checks

renaming inbox is special. the mailbox isn't renamed, but its messages moved to
a new mailbox. we weren't updating the destination mailbox uidnext with the new
messages. the fix not only sets the uidnext correctly, but also renumbers the
uids, starting at 1.

this also adds a consistency check for message uids and mailbox uidnexts, and
for mailbox uidvalidity account nextuidvalidity in "mox verifydata".

this also adds command "mox fixuidmeta" (not listed) that fixes up mailbox uidnext
and account uidvalidity. and command "mox reassignuids" that will renumber the
uids for either one or all mailboxes in an account.
This commit is contained in:
Mechiel Lukkien
2023-06-30 17:19:29 +02:00
parent 1e049a087d
commit 3173da5497
5 changed files with 222 additions and 13 deletions

View File

@ -34,6 +34,8 @@ on-disk message file and there are no unrecognized files. If option -fix is
specified, unrecognized message files are moved away. This may be needed after
a restore, because messages enqueued or delivered in the future may get those
message sequence numbers assigned and writing the message file would fail.
Consistency of message/mailbox UID, UIDNEXT and UIDVALIDITY is verified as
well.
Because verifydata opens the database files, schema upgrades may automatically
be applied. This can happen if you use a new mox release. It is useful to run
@ -218,12 +220,32 @@ possibly making them potentially no longer readable by the previous version.
// todo: add some kind of check for the bloom filter?
// Check that all messages in the database have a message file on disk.
// And check consistency of UIDs with the mailbox UIDNext, and check UIDValidity.
seen := map[string]struct{}{}
dbpath := filepath.Join(accdir, "index.db")
db, err := bstore.Open(ctxbg, dbpath, &bstore.Options{MustExist: true}, store.DBTypes...)
checkf(err, dbpath, "opening account database to check messages")
if err == nil {
err := bstore.QueryDB[store.Message](ctxbg, db).ForEach(func(m store.Message) error {
uidvalidity := store.NextUIDValidity{ID: 1}
if err := db.Get(ctxbg, &uidvalidity); err != nil {
checkf(err, dbpath, "missing nextuidvalidity")
}
mailboxUIDNexts := map[int64]store.UID{}
err := bstore.QueryDB[store.Mailbox](ctxbg, db).ForEach(func(mb store.Mailbox) error {
mailboxUIDNexts[mb.ID] = mb.UIDNext
if mb.UIDValidity >= uidvalidity.Next {
checkf(errors.New(`inconsistent uidvalidity for mailbox/account, see "mox fixuidmeta"`), dbpath, "mailbox id %d has uidvalidity %d >= account nextuidvalidity %d", mb.ID, mb.UIDValidity, uidvalidity.Next)
}
return nil
})
checkf(err, dbpath, "reading mailboxes to check uidnext consistency")
err = bstore.QueryDB[store.Message](ctxbg, db).ForEach(func(m store.Message) error {
if uidnext := mailboxUIDNexts[m.MailboxID]; m.UID >= uidnext {
checkf(errors.New(`inconsistent uidnext for message/mailbox, see "mox fixuidmeta"`), dbpath, "message id %d in mailbox id %d has uid %d >= mailbox uidnext %d", m.ID, m.MailboxID, m.UID, uidnext)
}
mp := store.MessagePath(m.ID)
seen[mp] = struct{}{}
p := filepath.Join(accdir, "msg", mp)