Re: Cyrus problems with folder-type in annotations.db missing first 4 characters after upgrade from Kolab2
Timotheus Pokorra <[email protected]>
| Newsgroups | gmane.comp.kde.devel.kolab,gmane.comp.kde.kolab.devel |
|---|---|
| Message-ID | <CAJ0=nQejOWO-JhR9r__v2qSdzHW=Eht6A_mWknKX5JopTgz1Qw@mail.gmail.com> |
Hello,
after more digging and debugging, I think I have understood the problem.
Have a look at the function that parses the values from the skiplist,
which is the format of annotations.db.
http://git.cyrusimap.org/cyrus-imapd/tree/imap/annotate.c?h=cyrus-imapd-2.4#n305
(by the way, cyrus-imapd-2.3 had the same implementation).
In the method split_attribs there are these lines:
memcpy(&tmp, data, sizeof(unsigned long));
attrib->size = (size_t) ntohl(tmp);
data += sizeof(unsigned long); /* skip to value */
But this means, on a 32 bit machine, the sizeof(unsigned long) is 4,
but on a 64 bit machine, it is 8.
This means: when reading a file on a 64 bit machine, the first 4 bytes
of the string are skipped, if the file was written on a 32 bit
machine.
Using the script http://oss.netfarm.it/download/skiplist.py, I was
able to see the difference in the annotation value:
skiplist written on 32 bit system:
Data size 32 (32)
Data String ^@^@^@^Lnote.default^@text/plain^@I^E¨ç
Data String Hex:
00:00:00:0c:6e:6f:74:65:2e:64:65:66:61:75:6c:74:00:74:65:78:74:2f:70:6c:61:69:6e:00:49:05:a8:e7
skiplist written on 64 bit system:
Data size 40 (40)
Data String ^@^@^@^L^@^@^@^@note.default^@text/plain^@Q<9f>^S<9a>^@^@^@^@
Data String Hex:
00:00:00:0c:00:00:00:00:6e:6f:74:65:2e:64:65:66:61:75:6c:74:00:74:65:78:74:2f:70:6c:61:69:6e:00:51:9f:13:9a:00:00:00:00
By the way, in trunk, it has this comment:
http://git.cyrusimap.org/cyrus-imapd/tree/imap/annotate.c#n888
/*
* Sigh...this is dumb. We take care to be machine independent by
* storing the length in network byte order...but the size of the
* length field depends on whether we're running on a 32b or 64b
* platform.
*/
memcpy(&tmp, data, sizeof(unsigned long));
data += sizeof(unsigned long); /* skip to value */
Unfortunately, the problem has been recognised, but not been resolved.
I am thinking now of a solution for the upgrade, that does not require
to connect to the old server.
I am wondering to modify the skiplist.py, and call for each value
containing kolab/folder-type:
kolab set-mailbox-metadata "user/test/[email protected]"
"/shared/vendor/kolab/folder-type" "note.default"
But other values need to be fixed as well, eg /vendor/kolab/activesync etc
A first version of the script is available here:
https://gist.github.com/tpokorra/5643410, and attached to this email
as well.
After that, it might be good to convert the fixed skiplist to
berkeley, and back to skiplist, to make the annotations.db smaller
again, since the old values are still stored in the database
otherwise.
/usr/lib/cyrus-imapd/cvt_cyrusdb /var/lib/imap/annotations.db skiplist
`pwd`/annotations-berkeley.db berkeley
/usr/lib/cyrus-imapd/cvt_cyrusdb `pwd`/annotations-berkeley.db
berkeley /var/lib/imap/annotations.db skiplist
I should probably update the upgrade instructions in the Administrator
Guide, at least explain the sentence "The annotations database may not
have been upgraded correctly, causing some annotations to miss the
first 4 characters of their value".
I have attached a patch file for the docs, for file
https://git.kolab.org/kolab-docs/tree/Administrator_Guide/en-US/Upgrading_from_Kolab_2_on_OpenPKG.xml
All the best,
Timotheus
On 22 May 2013 16:00, Timotheus Pokorra <[email protected]> wrote:
> Hello all,
>
> I am wondering about this issue:
> After you upgraded your Kolab2 IMAP data, on Kolab3 the command kolab
> list-mailbox-metadata will eg. show t.default instead of event.default
> for your calendar.
> Because of this, the calendar folder will not appear as the default
> calendar in Roundcube.
>
> This is documented in the Administrators Guide:
> http://docs.kolab.org/en-US/Kolab_Groupware/3.0/html/Administrator_Guide/chap-Administrator_Guide-Upgrading_from_Kolab_2_on_OpenPKG.html
> "The annotations database may not have been upgraded correctly,
> causing some annotations to miss the first 4 characters of their
> value"
>
> Following the advice in the guide to connect to the old server might
> work (where is the password for cyrus admin on Kolab2?), but I am
> still wondering about the reasons for this problem, and if there could
> be a cleaner solution.
>
> When I open on my Kolab3 server the file /var/lib/imap/annotations.db
> in vi, I can search for the folder types, and they seem ok to me,
> there I can see default.event etc.
>
> Related to this problem, I found also this post on the mailing list:
> http://www.intevation.de/pipermail/kolab-users/2012-March/013290.html
> "x32 to x64 convertion, Cyrus issue with annotations"
>
> When I try to convert the annotations.db from skiplist to flat:
> /usr/lib/cyrus-imapd/cvt_cyrusdb `pwd`/annotations.db skiplist
> `pwd`/test.db flat
> I get this error in maillog:
> cvt_cyrusdb[18569]: IOERROR: writing /root/annotations/test.db.NEW:
> Invalid argument
>
> I cannot find any suggestions for the reason of this error message on the web.
>
> Any ideas?
>
> Thanks,
> Timotheus
_______________________________________________
Kolab-devel mailing list
[email protected]
https://www.intevation.de/mailman/listinfo/kolab-devel
Upgrading_from_Kolab_2_on_OpenPKG.xml.patch
(application/octet-stream, 1.1 KB)
--- Upgrading_from_Kolab_2_on_OpenPKG.xml 2013-05-24 15:21:26.000000000 +0200
+++ Upgrading_from_Kolab_2_on_OpenPKG.xml.new 2013-05-24 14:23:27.262958187 +0200
@@ -760,7 +760,7 @@
<xi:include href="step-select-all-mailboxes.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<step>
<para>
- The annotations database may not have been upgraded correctly, causing some annotations to miss the first 4 characters of their value. The easiest way to fix the issue, that is known to work, is to get the annotation values as they were on the old (Kolab 2) IMAP server, and set them on the new (Kolab 3) IMAP server.
+ The annotations database may not have been upgraded correctly, causing some annotations to miss the first 4 characters of their value. This happens when converting from a 32 bit system to a 64 bit system. The easiest way to fix the issue, that is known to work, is to get the annotation values as they were on the old (Kolab 2) IMAP server, and set them on the new (Kolab 3) IMAP server.
</para>
<para>
skiplist_upgrade32_to_64bit.py
(application/octet-stream, 5.7 KB)
#!/usr/bin/env python # -*- Mode: Python; tab-width: 4 -*- # # Cyrus Imapd Skiplist db recovery tool # # Copyright (C) 2004-2006 Gianluigi Tiesi <[email protected]> # Copyright (C) 2004-2006 NetFarm S.r.l. [http://www.netfarm.it] # Copyright (C) 2013 TBits.net (http://www.tbits.net) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # ====================================================================== __version__= '0.2b' __doc__="""Cyrus skiplist db recover""" from sys import argv, stdout, stderr, exit as sys_exit from struct import unpack from time import localtime, strftime import os ### TODO: Correct handle COMMIT/DEL stuff ### right now this tools rougly dumps entries in the skiplist file ### Enable debug mode debug = 1 ### TIMEFMT ='%a, %d %b %Y %H:%M:%S %z' MAGIC = '\xa1\x02\x8b\x0d' PADDING = '\xff\xff\xff\xff' INORDER = 1 ADD = 2 DELETE = 4 COMMIT = 255 DUMMY = 257 HEADER = -1 MAIN = -2 types = { 1: 'INORDER', 2: 'ADD', 4: 'DELETE', 255: 'COMMIT', 257: 'DUMMY', -1: 'HEADER', -2: '*' } def log(rtype, text): global debug if debug: out = '[%s] %s\n' % (types[rtype], text) stdout.write(out) stdout.flush() def roundto4(value): if value % 4: return ((value / 4) + 1) * 4 return value def get_header(fp): magic = fp.read(4) if magic != MAGIC: log(HEADER, 'Magic signature mismatch') sign = fp.read(16) log(HEADER, sign[:-3]) version = unpack('>I', fp.read(4))[0] version_minor = unpack('>I', fp.read(4))[0] log(HEADER, 'Version %d,%d' % (version, version_minor)) maxlevel = unpack('>I', fp.read(4))[0] curlevel = unpack('>I', fp.read(4))[0] log(HEADER, 'Level %d/%d' % (curlevel, maxlevel)) listsize = unpack('>I', fp.read(4))[0] log(HEADER, 'List size %d' % listsize) logstart = unpack('>I', fp.read(4))[0] log(HEADER, 'Offset %d' % logstart) lastrecovery = localtime(unpack('>I', fp.read(4))[0]) lastrecovery = strftime(TIMEFMT, lastrecovery) log(HEADER, 'Last Recovery %s' % lastrecovery) return { 'version' : [version, version_minor], 'level' : [curlevel, maxlevel], 'listsize' : listsize, 'logstart' : logstart, 'lastrecover': lastrecovery } def getkeys(fp): values = [] keys = {} keystring = '' datastring = '' while 1: log(MAIN, '-' * 78) stype = fp.read(4) ### EOF if len(stype) != 4: break rtype = unpack('>I', stype)[0] if not types.has_key(rtype): log(MAIN, 'Invalid type %d' % rtype) continue log(rtype, 'Record type %s' % types[rtype]) if rtype == DELETE: ptr = unpack('>I', fp.read(4))[0] log(rtype, 'DELETE %d (0x%x)' % (ptr, ptr)) continue if rtype == COMMIT: continue ksize = unpack('>I', fp.read(4))[0] log(rtype, 'Key size %d (%d)' % (ksize, roundto4(ksize))) if ksize: keystring = fp.read(roundto4(ksize))[:ksize] log(rtype, 'Key String %s' % keystring) #log(rtype, 'Key String %s' % ':'.join(x.encode('hex') for x in keystring)) datasize = unpack('>I', fp.read(4))[0] log(rtype, 'Data size %d (%d)' % (datasize, roundto4(datasize))) if datasize: datastring = fp.read(roundto4(datasize))[:datasize] log(rtype, 'Data String %s' % datastring) #log(rtype, 'Data String %s' % ':'.join(x.encode('hex') for x in datastring)) firststring = datastring[4:datastring.find(chr(0), 4)] # check if the value has already been converted to 64 bit if len(firststring) > 0: log(rtype, 'First String %s' % firststring) domain = keystring[:keystring.find('!')] posFirstNullInKey = keystring.find(chr(0), len(domain)+1) folder = keystring[len(domain) + 1:posFirstNullInKey].replace('.', '/') annotationkey = keystring[posFirstNullInKey+1:keystring.find(chr(0), posFirstNullInKey + 1)] log (rtype, 'kolab set-mailbox-metadata "%s@%s" "%s" "%s"' % (folder, domain, annotationkey, firststring)) os.system ( 'kolab set-mailbox-metadata "%s@%s" "%s" "%s"' % (folder, domain, annotationkey, firststring)) n = 0 while 1: str_p = fp.read(4) if str_p == PADDING: break spointer = unpack('>I', str_p)[0] n = n +1 if spointer: log(rtype, 'Skip pointer %d' % spointer) log(rtype, 'Total Skip pointers: %d' % n) if rtype != DUMMY: if keystring not in values: values.append(keystring) keys[keystring] = datastring return values, keys if __name__ == '__main__': if len(argv) != 2: print 'Usage: %s skiplist.file' % argv[0] sys_exit() fp = open(argv[1], 'rb') header = get_header(fp) values, keys = getkeys(fp) fp.close() if debug: sys_exit() for v in values: print '%s\t%s' % (v, keys[v])