#define MAIL_TRANSACTION_LOG_MAJOR_VERSION 1
#define MAIL_TRANSACTION_LOG_MINOR_VERSION 3
+/* Minimum allowed mail_transaction_log_header.hdr_size. If it's smaller,
+ assume the file is corrupted. */
#define MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE 24
+/* Helper macro for other MAIL_TRANSACTION_LOG_VERSION_*() macros */
#define MAIL_TRANSACTION_LOG_VERSION_FULL(major, minor) \
((major) << 8 | (minor))
+/* Returns TRUE if the transaction log version supports the given feature.
+ The wanted_feature is one of the MAIL_TRANSACTION_LOG_VERSION_FEATURE_*
+ macros without the macro prefix, e.g. just COMPAT_FLAGS. */
#define MAIL_TRANSACTION_LOG_VERSION_HAVE(version, wanted_feature) \
((version) >= MAIL_TRANSACTION_LOG_VERSION_FEATURE_##wanted_feature)
+/* Returns transaction log version from the given mail_transaction_log_header
+ which is compatible for the MAIL_TRANSACTION_LOG_VERSION_HAVE() macro. */
#define MAIL_TRANSACTION_LOG_HDR_VERSION(hdr) \
MAIL_TRANSACTION_LOG_VERSION_FULL((hdr)->major_version, (hdr)->minor_version)
+/* Log feature: mail_transaction_log_header.compat_flags is filled. */
#define MAIL_TRANSACTION_LOG_VERSION_FEATURE_COMPAT_FLAGS \
MAIL_TRANSACTION_LOG_VERSION_FULL(1, 2)
+/* Log feature: Don't increase modseq when reading internal flag updates
+ (because they're not client-visible anyway).
+ See MAIL_TRANSACTION_FLAG_UPDATE_IS_INTERNAL(). */
#define MAIL_TRANSACTION_LOG_VERSION_FEATURE_HIDE_INTERNAL_MODSEQS \
MAIL_TRANSACTION_LOG_VERSION_FULL(1, 3)
struct mail_transaction_log_header {
+ /* Major version is increased only when you can't have backwards
+ compatibility. If the field doesn't match
+ MAIL_TRANSACTION_LOG_MAJOR_VERSION, don't even try to read it. */
uint8_t major_version;
+ /* Minor version is increased when the file format changes in a
+ backwards compatible way. */
uint8_t minor_version;
+ /* Size of the header. If it's larger than this struct, ignore any
+ unknown fields. If it's smaller, assume the rest of the fields
+ are 0. */
uint16_t hdr_size;
+ /* Unique index file ID, which must match the main index's indexid.
+ See mail_index_header.indexid. This is overwritten to be 0 if the
+ log file is marked as corrupted. */
uint32_t indexid;
+ /* Log file sequence number. Increased every time the log is rotated
+ and a new log is created. Using (file_seq, offset) uniquely
+ identifies a position in the transaction log. */
uint32_t file_seq;
+ /* The previous log file's sequence and offset when the log was
+ rotated. The offset should be the same as the previous log file's
+ size. If there was no previous log file, or if the index is being
+ reset, these are 0.
+
+ These are mainly useful to optimize syncing when the start position
+ is (prev_file_seq, prev_file_offset). Then it's it's already known
+ that the syncing can be started from this log file wihtout having
+ to open the previous log file only to realize that there is nothing
+ to sync. (Which could have also lead to an error if the .log.2 was
+ already deleted.) */
uint32_t prev_file_seq;
uint32_t prev_file_offset;
+ /* UNIX timestamp when this file was created. Used in determining when
+ to rotate the log file. */
uint32_t create_stamp;
- uint64_t initial_modseq; /* v1.1+ (note: log's major/minor version) */
-
- uint8_t compat_flags; /* enum mail_index_header_compat_flags, v1.2+ */
+ /* Modseq value at the beginning of this file. Some transaction records
+ increase the modseq value. (Only with log format v1.1+) */
+ uint64_t initial_modseq;
+
+ /* Same as enum mail_index_header_compat_flags. Needs
+ MAIL_TRANSACTION_LOG_VERSION_FEATURE_COMPAT_FLAGS. */
+ uint8_t compat_flags;
+ /* Unused fields to make the struct 64bit aligned. These can be used
+ to add more fields to the header. */
uint8_t unused[3];
- uint32_t unused2; /* so that this struct is 64bit aligned */
+ uint32_t unused2;
};
enum mail_transaction_type {
+ /* struct mail_transaction_expunge[] - Expunge the UIDs.
+ Must have MAIL_TRANSACTION_EXPUNGE_PROT ORed to this. Avoid using
+ this, use MAIL_TRANSACTION_EXPUNGE_GUID instead. */
MAIL_TRANSACTION_EXPUNGE = 0x00000001,
+ /* struct mail_index_record[] - Save new mails with given flags. */
MAIL_TRANSACTION_APPEND = 0x00000002,
+ /* struct mail_transaction_flag_update[] - Update message flags
+ (or just modseq). */
MAIL_TRANSACTION_FLAG_UPDATE = 0x00000004,
+ /* struct mail_transaction_header_update[] - Update the index's base
+ header (struct mail_index_header). */
MAIL_TRANSACTION_HEADER_UPDATE = 0x00000020,
+ /* struct mail_transaction_ext_intro - Start operations for the given
+ extension. This can be used to create a new extension or resize an
+ existing extension, but usually it is just used in front of the
+ other MAIL_TRANSACTION_EXT_* records to specify which extension
+ they're working with. */
MAIL_TRANSACTION_EXT_INTRO = 0x00000040,
+ /* struct mail_transaction_ext_reset - Reset the last intro extension
+ by changing its reset_id and optionally zeroing out its old data. */
MAIL_TRANSACTION_EXT_RESET = 0x00000080,
+ /* struct mail_transaction_ext_hdr_update[] - Update the last intro
+ extension's header. This might later become deprecated in favor of
+ supporting only MAIL_TRANSACTION_EXT_HDR_UPDATE32, but for now
+ it's still used for <64kB headers. */
MAIL_TRANSACTION_EXT_HDR_UPDATE = 0x00000100,
+ /* struct mail_transaction_ext_rec_update[] - Update the last intro
+ extension records for the given UIDs with given content. */
MAIL_TRANSACTION_EXT_REC_UPDATE = 0x00000200,
+ /* struct mail_transaction_keyword_update - Add/remove the specified
+ keyword to messages. */
MAIL_TRANSACTION_KEYWORD_UPDATE = 0x00000400,
+ /* struct mail_transaction_keyword_reset[] - Clear out all keywords
+ in specified messages. */
MAIL_TRANSACTION_KEYWORD_RESET = 0x00000800,
+ /* struct mail_transaction_ext_atomic_inc[] - Atomically increase or
+ decrease the last intro extension record. The record must be 1, 2,
+ 4 or 8 bytes. This can be used e.g. for refcount extensions. */
MAIL_TRANSACTION_EXT_ATOMIC_INC = 0x00001000,
+ /* struct mail_transaction_expunge_guid[] - Expunge given UID, but
+ first verify that it matches the given GUID. Must have
+ MAIL_TRANSACTION_EXPUNGE_PROT ORed to this. */
MAIL_TRANSACTION_EXPUNGE_GUID = 0x00002000,
MAIL_TRANSACTION_MODSEQ_UPDATE = 0x00008000,
+ /* struct mail_transaction_ext_hdr_update32[] - Update the last intro
+ extension's header. Used for >=64kB headers. See also
+ MAIL_TRANSACTION_EXT_HDR_UPDATE. This was added in Dovecot v2.0. */
MAIL_TRANSACTION_EXT_HDR_UPDATE32 = 0x00010000,
+ /* Index was marked as deleted using mail_index_set_deleted().
+ There is no record content for this. */
MAIL_TRANSACTION_INDEX_DELETED = 0x00020000,
+ /* Index was marked as undeleted using mail_index_set_undeleted().
+ There is no record content for this. */
MAIL_TRANSACTION_INDEX_UNDELETED = 0x00040000,
+ /* struct mail_transaction_boundary - Specifies a size of the following
+ records that must be treated as a single transaction. This works
+ so that the transaction log reading code stops if it finds that
+ there is a transaction whose size points outside the currently
+ existing file. An unfinished transaction is truncated away after the
+ next write to the log. FIXME: it would be better to rotate the
+ log instead of truncating it. */
MAIL_TRANSACTION_BOUNDARY = 0x00080000,
+ /* Mailbox attribute update. This is a bit complicated format:
+ - [+-][p-s]<name><NUL>
+ - "+" means attribute is set, "-" means unset
+ - "p" means private attribute, "s" means shared
+ - <name> is the attribute name
+ - This can repeat multiple times
+ - <NUL>
+ - 0..3 bytes padding for 32bit alignment
+ - For each attribute update an array of uint32_t integers:
+ - Update timestamp
+ - For each "+" only: Length of the attribute value.
+ */
MAIL_TRANSACTION_ATTRIBUTE_UPDATE = 0x00100000,
+ /* Mask to get the attribute type only (excluding flags). */
MAIL_TRANSACTION_TYPE_MASK = 0x0fffffff,
#define MAIL_TRANSACTION_EXT_MASK \
MAIL_TRANSACTION_EXT_HDR_UPDATE | MAIL_TRANSACTION_EXT_HDR_UPDATE32 | \
MAIL_TRANSACTION_EXT_REC_UPDATE | MAIL_TRANSACTION_EXT_ATOMIC_INC)
- /* since we'll expunge mails based on data read from transaction log,
+ /* Since we'll expunge mails based on data read from transaction log,
try to avoid the possibility of corrupted transaction log expunging
- messages. this value is ORed to the actual MAIL_TRANSACTION_EXPUNGE*
- flag. if it's not present, assume corrupted log. */
+ messages. This value is ORed to the actual MAIL_TRANSACTION_EXPUNGE*
+ flag. If it's not present, assume corrupted log. */
MAIL_TRANSACTION_EXPUNGE_PROT = 0x0000cd90,
- /* Mailbox storage backend synchronization noticed this change. */
+ /* External transactions have a bit different meanings depending on the
+ transaction type. Generally they mean to indicate changes that have
+ already occurred, instead of changes that are only being requested
+ to happen on next sync. For example expunges are first requested
+ to be done with internal transactions, and then there's a separate
+ external transaction to indicate that they were actually done. */
MAIL_TRANSACTION_EXTERNAL = 0x10000000,
/* This change syncs the state with another mailbox (dsync),
i.e. the change isn't something that a user requested locally. */
};
struct mail_transaction_header {
+ /* Size of this header and the following records. This size can be
+ used to calculate how many records there are. The size is written
+ via mail_index_uint32_to_offset(). */
uint32_t size;
uint32_t type; /* enum mail_transaction_type */
+ /* Header is followed by the type-specific records. */
};
+/* See MAIL_TRANSACTION_MODSEQ_UPDATE. */
struct mail_transaction_modseq_update {
uint32_t uid;
/* don't use uint64_t here. it adds extra 32 bits of padding and also
uint32_t modseq_high32;
};
+/* See MAIL_TRANSACTION_EXPUNGE. */
struct mail_transaction_expunge {
+ /* Expunge all mails between uid1..uid2. */
uint32_t uid1, uid2;
};
+/* See MAIL_TRANSACTION_EXPUNGE_GUID. */
struct mail_transaction_expunge_guid {
+ /* Expunge uid, but only if it matches guid_128. */
uint32_t uid;
+ /* GUID of the mail. If it's not 128 bit GUID, first pass it through
+ mail_generate_guid_128_hash() to get 128 bit SHA1 of it. */
guid_128_t guid_128;
};
+/* See MAIL_TRANSACTION_FLAG_UPDATE. */
struct mail_transaction_flag_update {
+ /* Change the flags for all mails between uid1..uid2. */
uint32_t uid1, uid2;
+ /* Add these flags to the mails. */
uint8_t add_flags;
+ /* Remove these flags to the mails. To replace all existing flags,
+ just set this to 0xff and specify the wanted flags in add_flags. */
uint8_t remove_flags;
+ /* If non-0, MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ was used to force
+ increasing modseq update to the mails even though no flags were
+ actually changed. This differs from MAIL_TRANSACTION_MODSEQ_UPDATE
+ in that the modseq is just wanted to be increased, doesn't matter
+ to which value specifically. */
uint8_t modseq_inc_flag;
+ /* Unused padding */
uint8_t padding;
};
+/* See MAIL_TRANSACTION_KEYWORD_UPDATE. */
struct mail_transaction_keyword_update {
- uint8_t modify_type; /* enum modify_type : MODIFY_ADD / MODIFY_REMOVE */
+ /* enum modify_type : MODIFY_ADD / MODIFY_REMOVE */
+ uint8_t modify_type;
uint8_t padding;
+ /* Size of name[] */
uint16_t name_size;
- /* unsigned char name[];
- array of { uint32_t uid1, uid2; }
- */
+ /* unsigned char name[name_size]; */
+ /* Update keywords for the given UIDs. The array's size is calculated
+ from mail_transaction_header.size. */
+ /* array of { uint32_t uid1, uid2; } */
};
+/* See MAIL_TRANSACTION_KEYWORD_RESET. */
struct mail_transaction_keyword_reset {
+ /* Clear out all keywords for uid1..uid2. */
uint32_t uid1, uid2;
};
+/* See MAIL_TRANSACTION_HEADER_UPDATE. */
struct mail_transaction_header_update {
+ /* Update start offset. */
uint16_t offset;
+ /* Size of the following data[] to update. */
uint16_t size;
- /* unsigned char data[]; */
+ /* unsigned char data[size]; */
+ /* 0..3 bytes of padding to get to 32bit alignment. */
+ /* unsigned char padding[]; */
};
enum {
MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK = 0x01
};
-/* See struct mail_index_ext_header for more explanations of these fields. */
+/* See MAIL_TRANSACTION_EXT_INTRO. Also see struct mail_index_ext_header for
+ more explanations of these fields. */
struct mail_transaction_ext_intro {
/* If extension is already known to exist in the index file,
set ext_id, but use empty name. If this is a new extension, set
/* unsigned char name[]; */
};
+/* See MAIL_TRANSACTION_EXT_RESET. */
struct mail_transaction_ext_reset {
+ /* New value for extension's reset_id */
uint32_t new_reset_id;
+ /* Non-0 if the old extension header and record data should be
+ preserved. Normally all of it is zeroed out. */
uint8_t preserve_data;
uint8_t unused_padding[3];
};
-/* these are set for the last ext_intro */
+/* See MAIL_TRANSACTION_EXT_HDR_UPDATE. */
struct mail_transaction_ext_hdr_update {
+ /* Update start offset. */
uint16_t offset;
+ /* Size of the following data[] to update. */
uint16_t size;
- /* unsigned char data[]; */
+ /* unsigned char data[size]; */
+ /* 0..3 bytes of padding to get to 32bit alignment. */
+ /* unsigned char padding[]; */
};
-/* this _update32 version should have been the only ext_hdr_update,
- but since 16bit integers were originally used for now we'll just use this
- only when actually needed to be backwards compatible. */
+/* See MAIL_TRANSACTION_EXT_HDR_UPDATE32. */
struct mail_transaction_ext_hdr_update32 {
+ /* Update start offset. */
uint32_t offset;
+ /* Size of the following data[] to update. */
uint32_t size;
- /* unsigned char data[]; */
+ /* unsigned char data[size]; */
+ /* 0..3 bytes of padding to get to 32bit alignment. */
+ /* unsigned char padding[]; */
};
+/* See MAIL_TRANSACTION_EXT_REC_UPDATE. */
struct mail_transaction_ext_rec_update {
uint32_t uid;
- /* unsigned char data[]; */
+ /* unsigned char data[mail_transaction_ext_intro.record_size]; */
+ /* 0..3 bytes of padding to get to 32bit alignment. */
+ /* unsigned char padding[]; */
};
+
+/* See MAIL_TRANSACTION_EXT_ATOMIC_INC. */
struct mail_transaction_ext_atomic_inc {
uint32_t uid;
+ /* Add this value to the extension record data. Can be negative. */
int32_t diff;
};
+/* See MAIL_TRANSACTION_BOUNDARY. */
struct mail_transaction_boundary {
+ /* Size of the whole transaction, including this record and header. */
uint32_t size;
};
struct mail_transaction_log_append_ctx {
struct mail_transaction_log *log;
+ /* All the changes that will be written to the transaction log. */
buffer_t *output;
+ /* Transaction flags as given to mail_transaction_log_append_begin(). */
enum mail_transaction_type trans_flags;
+ /* Tracking the current highest_modseq after the changes. This will
+ be used to update mail_transaction_log_file.sync_highest_modseq. */
uint64_t new_highest_modseq;
+ /* Number of transaction records added so far. */
unsigned int transaction_count;
- /* same as mail_index_transaction->sync_transaction */
+ /* Copied from mail_index_transaction.sync_transaction */
bool index_sync_transaction:1;
- /* same as mail_index_transaction->tail_offset_changed */
+ /* Copied from mail_index_transaction.tail_offset_changed */
bool tail_offset_changed:1;
+ /* TRUE if the mail_transaction_log_file has been synced up to the
+ current write offset, and we're writing a syncing transaction
+ (index_sync_transaction=TRUE). This means that the just written
+ transaction can be assumed to be synced already. */
bool sync_includes_this:1;
+ /* fdatasync() after writing the transaction. */
bool want_fsync:1;
};