1 #ifndef DBOX_FILE_H 2 #define DBOX_FILE_H 3 4 /* The file begins with a header followed by zero or more messages: 5 6 <dbox message header> 7 <LF> 8 <message body> 9 <metadata> 10 11 Metadata block begins with DBOX_MAGIC_POST, followed by zero or more lines 12 in format <key character><value><LF>. The block ends with an empty line. 13 Unknown metadata should be ignored, but preserved when copying. 14 15 There should be no duplicates for the current metadata, but future 16 extensions may need them so they should be preserved. 17 */ 18 #define DBOX_VERSION 2 19 #define DBOX_MAGIC_PRE "\001\002" 20 #define DBOX_MAGIC_POST "\n\001\003\n" 21 22 /* prefer flock(). fcntl() locking currently breaks if trying to access the 23 same file from multiple mail_storages within same process. that's why we 24 fallback to dotlocks. */ 25 #ifdef HAVE_FLOCK 26 # define DBOX_FILE_LOCK_METHOD_FLOCK 27 #endif 28 29 struct dbox_file; 30 struct stat; 31 32 enum dbox_header_key { 33 /* Must be sizeof(struct dbox_message_header) when appending (hex) */ 34 DBOX_HEADER_MSG_HEADER_SIZE = 'M', 35 /* Creation UNIX timestamp (hex) */ 36 DBOX_HEADER_CREATE_STAMP = 'C', 37 38 /* metadata used by old Dovecot versions */ 39 DBOX_HEADER_OLDV1_APPEND_OFFSET = 'A' 40 }; 41 42 /* NOTE: all valid keys are uppercase characters. if this changes, change 43 dbox-file-fix.c:dbox_file_match_post_magic() to recognize them */ 44 enum dbox_metadata_key { 45 /* Globally unique identifier for the message. Preserved when 46 copying. */ 47 DBOX_METADATA_GUID = 'G', 48 /* POP3 UIDL overriding the default format */ 49 DBOX_METADATA_POP3_UIDL = 'P', 50 /* POP3 message ordering (for migrated mails) */ 51 DBOX_METADATA_POP3_ORDER = 'O', 52 /* Received UNIX timestamp in hex */ 53 DBOX_METADATA_RECEIVED_TIME = 'R', 54 /* Physical message size in hex. Necessary only if it differs from 55 the dbox_message_header.message_size_hex, for example because the 56 message is compressed. */ 57 DBOX_METADATA_PHYSICAL_SIZE = 'Z', 58 /* Virtual message size in hex (line feeds counted as CRLF) */ 59 DBOX_METADATA_VIRTUAL_SIZE = 'V', 60 /* Pointer to external message data. Format is: 61 1*(<start offset> <byte count> <options> <ref>) */ 62 DBOX_METADATA_EXT_REF = 'X', 63 /* Mailbox name where this message was originally saved to. 64 When rebuild finds a message whose mailbox is unknown, it's 65 placed to this mailbox. */ 66 DBOX_METADATA_ORIG_MAILBOX = 'B', 67 68 /* metadata used by old Dovecot versions */ 69 DBOX_METADATA_OLDV1_EXPUNGED = 'E', 70 DBOX_METADATA_OLDV1_FLAGS = 'F', 71 DBOX_METADATA_OLDV1_KEYWORDS = 'K', 72 DBOX_METADATA_OLDV1_SAVE_TIME = 'S', 73 DBOX_METADATA_OLDV1_SPACE = ' ' 74 }; 75 76 enum dbox_message_type { 77 /* Normal message */ 78 DBOX_MESSAGE_TYPE_NORMAL = 'N' 79 }; 80 81 struct dbox_message_header { 82 unsigned char magic_pre[2]; 83 unsigned char type; 84 unsigned char space1; 85 unsigned char oldv1_uid_hex[8]; 86 unsigned char space2; 87 unsigned char message_size_hex[16]; 88 /* <space reserved for future extensions, LF is always last> */ 89 unsigned char save_lf; 90 }; 91 92 struct dbox_metadata_header { 93 unsigned char magic_post[sizeof(DBOX_MAGIC_POST)-1]; 94 }; 95 96 struct dbox_file { 97 struct dbox_storage *storage; 98 int refcount; 99 100 time_t create_time; 101 unsigned int file_version; 102 unsigned int file_header_size; 103 unsigned int msg_header_size; 104 105 const char *cur_path; 106 char *primary_path, *alt_path; 107 int fd; 108 struct istream *input; 109 #ifdef DBOX_FILE_LOCK_METHOD_FLOCK 110 struct file_lock *lock; 111 #else 112 struct dotlock *lock; 113 #endif 114 115 uoff_t cur_offset; 116 uoff_t cur_physical_size; 117 118 /* Metadata for the currently seeked metadata block. */ 119 pool_t metadata_pool; 120 ARRAY(const char *) metadata; 121 uoff_t metadata_read_offset; 122 123 bool appending:1; 124 bool corrupted:1; 125 }; 126 127 struct dbox_file_append_context { 128 struct dbox_file *file; 129 130 uoff_t first_append_offset, last_checkpoint_offset, last_flush_offset; 131 struct ostream *output; 132 }; 133 134 #define dbox_file_is_open(file) ((file)->fd != -1) 135 #define dbox_file_is_in_alt(file) ((file)->cur_path == (file)->alt_path) 136 137 void dbox_file_init(struct dbox_file *file); 138 void dbox_file_unref(struct dbox_file **file); 139 140 /* Open the file. Returns 1 if ok, 0 if file header is corrupted, -1 if error. 141 If file is deleted, deleted_r=TRUE and 1 is returned. */ 142 int dbox_file_open(struct dbox_file *file, bool *deleted_r); 143 /* Try to open file only from primary path. */ 144 int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r); 145 /* Close the file handle from the file, but don't free it. */ 146 void dbox_file_close(struct dbox_file *file); 147 148 /* fstat() or stat() the file. If file is already deleted, fails with 149 errno=ENOENT. */ 150 int dbox_file_stat(struct dbox_file *file, struct stat *st_r); 151 152 /* Try to lock the dbox file. Returns 1 if ok, 0 if already locked by someone 153 else, -1 if error. */ 154 int dbox_file_try_lock(struct dbox_file *file); 155 void dbox_file_unlock(struct dbox_file *file); 156 157 /* Seek to given offset in file. Returns 1 if ok/expunged, 0 if file/offset is 158 corrupted, -1 if I/O error. */ 159 int dbox_file_seek(struct dbox_file *file, uoff_t offset); 160 /* Start seeking at the beginning of the file. */ 161 void dbox_file_seek_rewind(struct dbox_file *file); 162 /* Seek to next message after current one. If there are no more messages, 163 returns 0 and last_r is set to TRUE. Returns 1 if ok, 0 if file is 164 corrupted, -1 if I/O error. */ 165 int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r); 166 167 /* Start appending to dbox file */ 168 struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file); 169 /* Finish writing appended mails. */ 170 int dbox_file_append_commit(struct dbox_file_append_context **ctx); 171 /* Truncate appended mails. */ 172 void dbox_file_append_rollback(struct dbox_file_append_context **ctx); 173 /* Get output stream for appending a new message. Returns 1 if ok, 0 if file 174 can't be appended to (old file version or corruption) or -1 if error. */ 175 int dbox_file_get_append_stream(struct dbox_file_append_context *ctx, 176 struct ostream **output_r); 177 /* Call after message has been fully saved. If this isn't done, the writes 178 since the last checkpoint are truncated. */ 179 void dbox_file_append_checkpoint(struct dbox_file_append_context *ctx); 180 /* Flush output buffer. */ 181 int dbox_file_append_flush(struct dbox_file_append_context *ctx); 182 183 /* Read current message's metadata. Returns 1 if ok, 0 if metadata is 184 corrupted, -1 if I/O error. */ 185 int dbox_file_metadata_read(struct dbox_file *file); 186 /* Return wanted metadata value, or NULL if not found. */ 187 const char *dbox_file_metadata_get(struct dbox_file *file, 188 enum dbox_metadata_key key); 189 190 /* Returns DBOX_METADATA_PHYSICAL_SIZE if set, otherwise physical size from 191 header. They differ only for e.g. compressed mails. */ 192 uoff_t dbox_file_get_plaintext_size(struct dbox_file *file); 193 194 /* Fix a broken dbox file by rename()ing over it with a fixed file. Everything 195 before start_offset is assumed to be valid and is simply copied. The file 196 is reopened afterwards. Returns 1 if ok, 0 if the resulting file has no 197 mails and was deleted, -1 if I/O error. */ 198 int dbox_file_fix(struct dbox_file *file, uoff_t start_offset); 199 /* Delete the given dbox file. Returns 1 if deleted, 0 if file wasn't found 200 or -1 if error. */ 201 int dbox_file_unlink(struct dbox_file *file); 202 203 /* Fill dbox_message_header with given size. */ 204 void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr, 205 uoff_t message_size); 206 207 void dbox_file_set_syscall_error(struct dbox_file *file, const char *function); 208 void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...) 209 ATTR_FORMAT(2, 3); 210 211 /* private: */ 212 const char *dbox_generate_tmp_filename(void); 213 void dbox_file_free(struct dbox_file *file); 214 int dbox_file_header_write(struct dbox_file *file, struct ostream *output); 215 int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r); 216 int dbox_file_metadata_skip_header(struct dbox_file *file); 217 218 #endif 219