From b47528cc4bb7a308c74d4dae7c2adcf9f7392f4e Mon Sep 17 00:00:00 2001 From: Lakshmipathi Date: Fri, 8 Jul 2011 11:51:53 +0530 Subject: Added community provided scripts for lock and rename --- community-scripts/rename/index.c | 333 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 community-scripts/rename/index.c (limited to 'community-scripts/rename/index.c') diff --git a/community-scripts/rename/index.c b/community-scripts/rename/index.c new file mode 100644 index 0000000..4382095 --- /dev/null +++ b/community-scripts/rename/index.c @@ -0,0 +1,333 @@ +/* + gcc index.c -o index -Wall -g + ./index & + ./index & + ./index & + + abort()s on failure, runs forever on non-failure +*/ +#define _XOPEN_SOURCE 500 /* for pread() */ +#define _BSD_SOURCE /* for major(), minor() */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mail_index { + const char *filepath; + int fd; + int log_fd; +}; + +struct mail_index_header { + /* major version is increased only when you can't have backwards + compatibility. minor version is increased when header size is + increased to contain new non-critical fields. */ + uint8_t major_version; + uint8_t minor_version; + + uint16_t base_header_size; + uint32_t header_size; /* base + extended header size */ + uint32_t record_size; + + uint8_t compat_flags; /* enum mail_index_header_compat_flags */ + uint8_t unused[3]; + + uint32_t indexid; + uint32_t flags; + + uint32_t uid_validity; + uint32_t next_uid; + + uint32_t messages_count; + uint32_t unused_old_recent_messages_count; + uint32_t seen_messages_count; + uint32_t deleted_messages_count; + + uint32_t first_recent_uid; + /* these UIDs may not exist and may not even be unseen/deleted */ + uint32_t first_unseen_uid_lowwater; + uint32_t first_deleted_uid_lowwater; + + uint32_t log_file_seq; + /* non-external records between tail..head haven't been committed to + mailbox yet. */ + uint32_t log_file_tail_offset; + uint32_t log_file_head_offset; + + uint64_t sync_size; + uint32_t sync_stamp; + + /* daily first UIDs that have been added to index. */ + uint32_t day_stamp; + uint32_t day_first_uid[8]; +}; + +struct mail_index_record { + uint32_t uid; + uint8_t flags; /* enum mail_flags | enum mail_index_mail_flags */ +}; +int mail_index_try_open_only(struct mail_index *index) +{ + index->fd = open(index->filepath, O_RDWR); + if (index->fd == -1) { + if (errno != ENOENT) + abort(); + + /* have to create it */ + return 0; + } + return 1; +} + +void mail_index_close_file(struct mail_index *index) +{ + if (index->fd != -1) { + if (close(index->fd) < 0) + abort(); + index->fd = -1; + } +} + +int mail_index_reopen_if_changed(struct mail_index *index) +{ + struct stat st1, st2; + + if (index->fd == -1) + return mail_index_try_open_only(index); + + //nfs_flush_file_handle_cache(index->filepath); + if (stat(index->filepath, &st2) < 0) + abort(); + +#define CMP_DEV_T(a, b) (major(a) == major(b) && minor(a) == minor(b)) + if (fstat(index->fd, &st1) < 0) { + abort(); + } else if (st1.st_ino == st2.st_ino && + CMP_DEV_T(st1.st_dev, st2.st_dev)) { + /* the same file */ + return 1; + } + + /* new file */ + mail_index_close_file(index); + return mail_index_try_open_only(index); +} + +static int mail_index_read_header(struct mail_index *index, + void *buf, size_t buf_size, size_t *pos_r) +{ + size_t pos; + int ret; + + memset(buf, 0, sizeof(struct mail_index_header)); + + pos = 0; + do { + ret = pread(index->fd, (char*)buf + pos, + buf_size - pos, pos); + if (ret <= 0) + abort(); + if (ret > 0) + pos += ret; + } while (ret > 0 && pos < sizeof(struct mail_index_header)); + + *pos_r = pos; + return ret; +} + +static int +mail_index_read_map(struct mail_index *index, off_t file_size) +{ + const struct mail_index_header *hdr; + unsigned char read_buf[8192]; + const void *buf; + ssize_t ret; + size_t pos, records_size; + unsigned int records_count = 0, extra; + + ret = mail_index_read_header(index, read_buf, sizeof(read_buf), &pos); + buf = read_buf; hdr = buf; + + if (ret > 0) { + /* header read, read the records now. */ + records_size = (size_t)hdr->messages_count * hdr->record_size; + records_count = hdr->messages_count; + + if (file_size - hdr->header_size < records_size || + (hdr->record_size != 0 && + records_size / hdr->record_size != hdr->messages_count)) + abort(); + + if (pos <= hdr->header_size) + extra = 0; + else + extra = pos - hdr->header_size; + if (records_size > extra) { + void *data = malloc(records_size - extra); + ret = pread(index->fd, data, records_size - extra, + hdr->header_size + extra); + if (ret != records_size - extra) abort(); + free(data); + } + } + return 1; +} + +static int file_lock_do(int fd, int lock_type, int timeout_secs) +{ + struct flock fl; + int ret; + + fl.l_type = lock_type; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + if (timeout_secs != 0) + alarm(timeout_secs); + ret = fcntl(fd, timeout_secs != 0 ? F_SETLKW : F_SETLK, &fl); + if (timeout_secs != 0) + alarm(0); + + if (ret == 0) + return 1; + + if (timeout_secs == 0 && + (errno == EACCES || errno == EAGAIN)) { + /* locked by another process */ + return 0; + } + abort(); +} + +static int mail_index_map_latest_file(struct mail_index *index) +{ + struct stat st; + off_t file_size; + int ret; + + ret = mail_index_reopen_if_changed(index); + if (ret <= 0) { + if (ret < 0) + return -1; + + /* the index file is lost/broken. */ + return 1; + } + + if (file_lock_do(index->fd, F_RDLCK, 120) == 0) abort(); + + //nfs_flush_attr_cache_fd_locked(index->filepath, index->fd); + if (fstat(index->fd, &st) < 0) + abort(); + file_size = st.st_size; + + ret = mail_index_read_map(index, file_size); + if (file_lock_do(index->fd, F_UNLCK, 0) == 0) abort(); + return 1; +} + +static int mail_index_create_backup(struct mail_index *index) +{ + char backup_path[1024], tmp_backup_path[1024]; + int ret; + + snprintf(backup_path, sizeof(backup_path), "%s.backup", index->filepath); + snprintf(tmp_backup_path, sizeof(tmp_backup_path), "%s.tmp", backup_path); + ret = link(index->filepath, tmp_backup_path); + if (ret < 0 && errno == EEXIST) { + if (unlink(tmp_backup_path) < 0 && errno != ENOENT) + abort(); + ret = link(index->filepath, tmp_backup_path); + } + if (ret < 0) { + if (errno == ENOENT) { + /* no dovecot.index file, ignore */ + return 0; + } + abort(); + } + + if (rename(tmp_backup_path, backup_path) < 0) + abort(); + return 0; +} + +static void mail_index_recreate(struct mail_index *index) +{ + struct mail_index_header hdr; + struct mail_index_record *recs; + char path[1024]; + int fd; + unsigned int i, size; + + snprintf(path, sizeof(path), "%s.tmp", index->filepath); + fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd == -1) + abort(); + + memset(&hdr, 0, sizeof(hdr)); + hdr.base_header_size = hdr.header_size = sizeof(hdr); + hdr.record_size = sizeof(*recs); + hdr.messages_count = (rand() % 10000) * 100; + + if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) abort(); + size = sizeof(*recs) * hdr.messages_count; + recs = calloc(size, 1); + for (i = 0; i < hdr.messages_count; i++) + recs[i].uid = i + 1; + if (write(fd, recs, size) != size) abort(); + free(recs); + if (fdatasync(fd) < 0) + abort(); + if (close(fd) < 0) + abort(); + mail_index_create_backup(index); + + if (rename(path, index->filepath) < 0) { + perror("rename()"); + abort(); + } +} + +static int log_lock(struct mail_index *index) +{ + if (index->log_fd == -1) { + index->log_fd = open("/mnt/gluster/dovecot.index.log", O_CREAT | O_RDWR, 0600); + if (index->log_fd == -1) + abort(); + } + return file_lock_do(index->log_fd, F_WRLCK, 0); +} + +static void log_unlock(struct mail_index *index) +{ + if (file_lock_do(index->log_fd, F_UNLCK, 0) == 0) abort(); +} + +int main(void) +{ + struct mail_index index; + + memset(&index, 0, sizeof(index)); + index.fd = -1; + index.log_fd = -1; + index.filepath = "/mnt/gluster/dovecot.index"; + + for (;;) { + if (rand() % 100 < 70) + mail_index_map_latest_file(&index); + else if (log_lock(&index) > 0) { + mail_index_recreate(&index); + log_unlock(&index); + } + usleep(10000); + } +} -- cgit