Mercurial > dovecot > original-hg > dovecot-1.2
changeset 6829:dbab5e592577 HEAD
NFS cache flushing APIs changed and backend implements it a bit differently.
Hopefully this works now more correctly.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 18 Nov 2007 08:40:59 +0200 |
parents | 8470d0845fc2 |
children | 3a41003bb1ae |
files | src/lib-index/mail-cache-compress.c src/lib-index/mail-cache-private.h src/lib-index/mail-cache-sync-update.c src/lib-index/mail-cache.c src/lib-index/mail-hash.c src/lib-index/mail-index-lock.c src/lib-index/mail-index-map.c src/lib-index/mail-index-private.h src/lib-index/mail-index.c src/lib-index/mail-transaction-log-file.c src/lib-index/mail-transaction-log.c src/lib-index/mailbox-list-index-sync.c src/lib-index/mailbox-list-index.c src/lib-storage/index/maildir/maildir-keywords.c src/lib-storage/index/maildir/maildir-uidlist.c src/lib-storage/index/mbox/mbox-lock.c src/lib-storage/mailbox-list.c src/lib/file-dotlock.c src/lib/nfs-workarounds.c src/lib/nfs-workarounds.h |
diffstat | 20 files changed, 243 insertions(+), 214 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-index/mail-cache-compress.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-cache-compress.c Sun Nov 18 08:40:59 2007 +0200 @@ -354,9 +354,6 @@ unsigned int i, count; int fd, ret; - if (!MAIL_CACHE_IS_UNUSABLE(cache)) - mail_cache_flush_read_cache(cache, TRUE); - /* get the latest info on fields */ if (mail_cache_header_fields_read(cache) < 0) return -1; @@ -460,6 +457,11 @@ if (cache->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) { /* we're using dotlocking, cache file creation itself creates the dotlock file we need. */ + if (!MAIL_CACHE_IS_UNUSABLE(cache)) { + mail_index_flush_read_cache(cache->index, + cache->filepath, cache->fd, + FALSE); + } return mail_cache_compress_locked(cache, trans, &unlock); }
--- a/src/lib-index/mail-cache-private.h Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-cache-private.h Sun Nov 18 08:40:59 2007 +0200 @@ -220,7 +220,6 @@ /* Returns -1 if cache is / just got corrupted, 0 if ok. */ int mail_cache_unlock(struct mail_cache *cache); -void mail_cache_flush_read_cache(struct mail_cache *cache, bool just_locked); int mail_cache_write(struct mail_cache *cache, const void *data, size_t size, uoff_t offset);
--- a/src/lib-index/mail-cache-sync-update.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-cache-sync-update.c Sun Nov 18 08:40:59 2007 +0200 @@ -11,7 +11,7 @@ unsigned int locked:1; unsigned int lock_failed:1; - unsigned int nfs_attr_cache_flushed:1; + unsigned int nfs_read_cache_flushed:1; }; static void mail_cache_handler_deinit(struct mail_index_sync_map_ctx *sync_ctx, @@ -127,10 +127,12 @@ ctx = mail_cache_handler_init(context); if (cache->file_cache != NULL && !MAIL_CACHE_IS_UNUSABLE(cache)) { - /* flush attribute cache only once per sync */ - if (!ctx->nfs_attr_cache_flushed && cache->index->nfs_flush) { - ctx->nfs_attr_cache_flushed = TRUE; - mail_cache_flush_read_cache(cache, FALSE); + /* flush read cache only once per sync */ + if (!ctx->nfs_read_cache_flushed && cache->index->nfs_flush) { + ctx->nfs_read_cache_flushed = TRUE; + mail_index_flush_read_cache(cache->index, + cache->filepath, cache->fd, + cache->locked); } /* don't invalidate anything that's already been invalidated within this sync. */ @@ -177,7 +179,11 @@ void mail_cache_sync_lost_handler(struct mail_index *index) { - if (!MAIL_CACHE_IS_UNUSABLE(index->cache)) - mail_cache_flush_read_cache(index->cache, FALSE); - file_cache_invalidate(index->cache->file_cache, 0, (uoff_t)-1); + struct mail_cache *cache = index->cache; + + if (!MAIL_CACHE_IS_UNUSABLE(cache)) { + mail_index_flush_read_cache(cache->index, cache->filepath, + cache->fd, cache->locked); + } + file_cache_invalidate(cache->file_cache, 0, (uoff_t)-1); }
--- a/src/lib-index/mail-cache.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-cache.c Sun Nov 18 08:40:59 2007 +0200 @@ -78,12 +78,6 @@ if (cache->file_cache == NULL) return; - if (cache->index->nfs_flush) { - nfs_flush_attr_cache_fd(cache->filepath, cache->fd); - nfs_flush_read_cache(cache->filepath, cache->fd, - F_UNLCK, FALSE); - } - file_cache_set_fd(cache->file_cache, cache->fd); if (fstat(cache->fd, &st) == 0) @@ -114,22 +108,33 @@ return TRUE; /* see if the file has changed */ - if (cache->index->nfs_flush) - nfs_flush_attr_cache(cache->filepath); + if (cache->index->nfs_flush) { + i_assert(!cache->locked); + nfs_flush_attr_cache_unlocked(cache->filepath); + } if (nfs_safe_stat(cache->filepath, &st) < 0) { mail_cache_set_syscall_error(cache, "stat()"); return TRUE; } + + if (st.st_ino != cache->st_ino || + !CMP_DEV_T(st.st_dev, cache->st_dev)) { + /* file changed */ + return TRUE; + } + if (cache->index->nfs_flush) { /* if the old file has been deleted, the new file may have - the same inode as the old one, but we'll catch this by - flushing attribute cache and seeing if it fails with - ESTALE */ - if (!nfs_flush_attr_cache_fd(cache->filepath, cache->fd)) - return TRUE; + the same inode as the old one. we'll catch this here by + checking if fstat() fails with ESTALE */ + if (fstat(cache->fd, &st) < 0) { + if (errno == ESTALE) + return TRUE; + mail_cache_set_syscall_error(cache, "fstat()"); + return FALSE; + } } - return st.st_ino != cache->st_ino || - !CMP_DEV_T(st.st_dev, cache->st_dev); + return FALSE; } int mail_cache_reopen(struct mail_cache *cache) @@ -450,23 +455,6 @@ i_free(cache); } -void mail_cache_flush_read_cache(struct mail_cache *cache, bool just_locked) -{ - if (!cache->index->nfs_flush) - return; - - /* Assume flock() is independent of fcntl() locks. This isn't true - with Linux 2.6 NFS, but with it there's no point in using flock() */ - if (cache->locked && - cache->index->lock_method == FILE_LOCK_METHOD_FCNTL) { - nfs_flush_read_cache(cache->filepath, cache->fd, - F_WRLCK, just_locked); - } else { - nfs_flush_read_cache(cache->filepath, cache->fd, - F_UNLCK, FALSE); - } -} - static int mail_cache_lock_file(struct mail_cache *cache) { int ret; @@ -490,10 +478,8 @@ if (ret <= 0) return ret; - if (cache->index->nfs_flush && - cache->index->lock_method != FILE_LOCK_METHOD_FCNTL) - nfs_flush_attr_cache_fd(cache->filepath, cache->fd); - mail_cache_flush_read_cache(cache, TRUE); + mail_index_flush_read_cache(cache->index, cache->filepath, cache->fd, + TRUE); return 1; }
--- a/src/lib-index/mail-hash.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-hash.c Sun Nov 18 08:40:59 2007 +0200 @@ -242,6 +242,8 @@ { int ret; + i_assert(hash->locked); + if (hash->mmap_base == NULL) { if (file_size < size) file_size = size; @@ -269,9 +271,6 @@ hash->mmap_size = size; } - if (hash->index->nfs_flush) - nfs_flush_read_cache(hash->filepath, hash->fd, F_UNLCK, FALSE); - ret = pread_full(hash->fd, hash->mmap_base, size, 0); if (ret < 0) { mail_hash_set_syscall_error(hash, "pread_full()"); @@ -356,8 +355,7 @@ struct stat st; size_t size; - if (hash->index->nfs_flush) - nfs_flush_attr_cache_fd(hash->filepath, hash->fd); + i_assert(hash->locked); if (fstat(hash->fd, &st) < 0) { mail_hash_set_syscall_error(hash, "fstat()"); @@ -408,23 +406,29 @@ static int mail_hash_file_lock(struct mail_hash *hash, int lock_type) { + int ret; + i_assert(hash->fd != -1); if (hash->index->lock_method != FILE_LOCK_METHOD_DOTLOCK) { i_assert(hash->file_lock == NULL); - return mail_index_lock_fd(hash->index, hash->filepath, hash->fd, - lock_type, MAIL_HASH_TIMEOUT_SECS, - &hash->file_lock); + ret = mail_index_lock_fd(hash->index, hash->filepath, hash->fd, + lock_type, MAIL_HASH_TIMEOUT_SECS, + &hash->file_lock); } else { i_assert(hash->dotlock == NULL); - if (file_dotlock_create(&hash->dotlock_settings, - hash->filepath, 0, - &hash->dotlock) < 0) { - mail_hash_set_syscall_error(hash, "open()"); - return -1; + ret = file_dotlock_create(&hash->dotlock_settings, + hash->filepath, 0, &hash->dotlock); + if (ret < 0) { + mail_hash_set_syscall_error(hash, + "file_dotlock_create()"); } - return 0; } + if (ret > 0) { + mail_index_flush_read_cache(hash->index, hash->filepath, + hash->fd, TRUE); + } + return ret; } static void mail_hash_file_unlock(struct mail_hash *hash) @@ -653,20 +657,25 @@ return mail_hash_reopen(hash); if (hash->index->nfs_flush) - nfs_flush_attr_cache(hash->filepath); + nfs_flush_attr_cache_unlocked(hash->filepath); if (nfs_safe_stat(hash->filepath, &st) < 0) { - if (errno == ENOENT) - return mail_hash_reopen(hash); - - if (errno != ESTALE) { + if (errno != ENOENT) { mail_hash_set_syscall_error(hash, "stat()"); return -1; } - /* if ESTALE is returned, it most likely means it was rebuilt */ - } else { - if (st.st_ino == hash->ino && CMP_DEV_T(st.st_dev, hash->dev)) + } else if (st.st_ino == hash->ino && + CMP_DEV_T(st.st_dev, hash->dev)) { + /* the file looks the same */ + if (!hash->index->nfs_flush) return 0; + + if (fstat(hash->fd, &st) == 0) + return 0; + if (errno != ESTALE) { + mail_hash_set_syscall_error(hash, "fstat()"); + return -1; + } } return mail_hash_reopen(hash); } @@ -894,6 +903,7 @@ { uint32_t hash_key = key == NULL ? 0 : hash->key_hash_cb(key); + i_assert(hash->locked); i_assert((key == NULL && hash->rec_hash_cb(value) == 0) || (key != NULL && hash_key != 0 && hash->rec_hash_cb(value) != 0));
--- a/src/lib-index/mail-index-lock.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-index-lock.c Sun Nov 18 08:40:59 2007 +0200 @@ -120,19 +120,20 @@ return 1; } -static void mail_index_flush_read_cache(struct mail_index *index) +void mail_index_flush_read_cache(struct mail_index *index, const char *path, + int fd, bool locked) { if (!index->nfs_flush) return; - /* Assume flock() is independent of fcntl() locks. This isn't true - with Linux 2.6 NFS, but with it there's no point in using flock() */ - if (index->lock_method == FILE_LOCK_METHOD_FCNTL) { - nfs_flush_read_cache(index->filepath, index->fd, - index->lock_type, TRUE); + /* Assume flock() is emulated with fcntl(), because that's how most + OSes work nowadays. */ + if (locked && + (index->lock_method == FILE_LOCK_METHOD_FCNTL || + index->lock_method == FILE_LOCK_METHOD_FLOCK)) { + nfs_flush_read_cache_locked(path, fd); } else { - nfs_flush_read_cache(index->filepath, index->fd, - F_UNLCK, FALSE); + nfs_flush_read_cache_unlocked(path, fd); } } @@ -143,7 +144,8 @@ ret = mail_index_lock(index, F_RDLCK, MAIL_INDEX_SHARED_LOCK_TIMEOUT, lock_id_r); if (ret > 0) { - mail_index_flush_read_cache(index); + mail_index_flush_read_cache(index, index->filepath, + index->fd, TRUE); return 0; } if (ret < 0) @@ -161,8 +163,10 @@ { int ret; - if ((ret = mail_index_lock(index, F_WRLCK, 0, lock_id_r)) > 0) - mail_index_flush_read_cache(index); + if ((ret = mail_index_lock(index, F_WRLCK, 0, lock_id_r)) > 0) { + mail_index_flush_read_cache(index, index->filepath, + index->fd, TRUE); + } return ret; }
--- a/src/lib-index/mail-index-map.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-index-map.c Sun Nov 18 08:40:59 2007 +0200 @@ -811,8 +811,8 @@ if (mail_index_lock_shared(index, &lock_id) < 0) return -1; - if (index->nfs_flush && index->lock_method != FILE_LOCK_METHOD_FCNTL) - nfs_flush_attr_cache_fd(index->filepath, index->fd); + if (index->nfs_flush) + nfs_flush_attr_cache_fd_locked(index->filepath, index->fd); if (fstat(index->fd, &st) == 0) file_size = st.st_size;
--- a/src/lib-index/mail-index-private.h Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-index-private.h Sun Nov 18 08:40:59 2007 +0200 @@ -244,6 +244,9 @@ /* Update/rewrite the main index file from index->map */ void mail_index_write(struct mail_index *index, bool want_rotate); +void mail_index_flush_read_cache(struct mail_index *index, const char *path, + int fd, bool locked); + /* Returns 0 = ok, -1 = error. */ int mail_index_lock_shared(struct mail_index *index, unsigned int *lock_id_r); /* Returns 1 = ok, 0 = already locked, -1 = error. */
--- a/src/lib-index/mail-index.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-index.c Sun Nov 18 08:40:59 2007 +0200 @@ -248,9 +248,6 @@ i_assert(index->fd == -1); i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); - if (index->nfs_flush) - nfs_flush_attr_cache(index->filepath); - /* Note that our caller must close index->fd by itself. */ if (index->readonly) errno = EACCES; @@ -465,6 +462,7 @@ { struct stat st1, st2; + i_assert(index->shared_lock_count == 0 || !index->nfs_flush); i_assert(index->excl_lock_count == 0); if (MAIL_INDEX_IS_IN_MEMORY(index)) @@ -474,30 +472,19 @@ return mail_index_try_open_only(index); if (index->nfs_flush) - nfs_flush_attr_cache(index->filepath); + nfs_flush_attr_cache_unlocked(index->filepath); if (nfs_safe_stat(index->filepath, &st2) < 0) { if (errno == ENOENT) return 0; - return mail_index_set_syscall_error(index, "stat()"); } - if (index->nfs_flush) { - if (!nfs_flush_attr_cache_fd(index->filepath, index->fd)) { - /* deleted/recreated, reopen */ - mail_index_close_file(index); - return mail_index_try_open_only(index); - } - } if (fstat(index->fd, &st1) < 0) { if (errno != ESTALE) return mail_index_set_syscall_error(index, "fstat()"); /* deleted/recreated, reopen */ - mail_index_close_file(index); - return mail_index_try_open_only(index); - } - - if (st1.st_ino == st2.st_ino && CMP_DEV_T(st1.st_dev, st2.st_dev)) { + } else if (st1.st_ino == st2.st_ino && + CMP_DEV_T(st1.st_dev, st2.st_dev)) { /* the same file */ return 1; }
--- a/src/lib-index/mail-transaction-log-file.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-transaction-log-file.c Sun Nov 18 08:40:59 2007 +0200 @@ -435,7 +435,7 @@ bool rename_existing; if (index->nfs_flush) - nfs_flush_attr_cache(file->filepath); + nfs_flush_attr_cache_unlocked(file->filepath); /* log creation is locked now - see if someone already created it. note that if we're rotating, we need to keep the log locked until @@ -794,11 +794,9 @@ return -1; } - if (file->log->index->nfs_flush) { - /* The size field will be updated soon */ - nfs_flush_read_cache(file->filepath, file->fd, - F_UNLCK, FALSE); - } + /* The size field will be updated soon */ + mail_index_flush_read_cache(file->log->index, file->filepath, + file->fd, file->locked); } if (file->next != NULL && @@ -920,7 +918,7 @@ if file is locked, the attribute cache was already flushed when refreshing the log. */ if (file->log->index->nfs_flush && nfs_flush) - nfs_flush_attr_cache_fd(file->filepath, file->fd); + nfs_flush_attr_cache_fd_locked(file->filepath, file->fd); if (file->buffer != NULL && file->buffer_offset > start_offset) { /* we have to insert missing data to beginning of buffer */
--- a/src/lib-index/mail-transaction-log.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mail-transaction-log.c Sun Nov 18 08:40:59 2007 +0200 @@ -279,8 +279,12 @@ path = t_strconcat(log->index->filepath, MAIL_TRANSACTION_LOG_SUFFIX, NULL); - if (log->index->nfs_flush && nfs_flush) - nfs_flush_attr_cache(path); + if (log->index->nfs_flush && nfs_flush) { + if (!log->head->locked) + nfs_flush_attr_cache_unlocked(path); + else + nfs_flush_attr_cache_dir(log->index->dir); + } if (nfs_safe_stat(path, &st) < 0) { if (errno != ENOENT) { mail_index_file_set_syscall_error(log->index, path,
--- a/src/lib-index/mailbox-list-index-sync.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mailbox-list-index-sync.c Sun Nov 18 08:40:59 2007 +0200 @@ -365,10 +365,8 @@ MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES) < 0) return -1; - if (ctx->index->mail_index->nfs_flush) { - nfs_flush_read_cache(ctx->index->filepath, ctx->index->fd, - F_UNLCK, FALSE); - } + mail_index_flush_read_cache(ctx->index->mail_index, ctx->index->filepath, + ctx->index->fd, FALSE); /* we should have only external transactions in here, for which we don't need to do anything but write them to the index */
--- a/src/lib-index/mailbox-list-index.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-index/mailbox-list-index.c Sun Nov 18 08:40:59 2007 +0200 @@ -58,11 +58,6 @@ static void mailbox_list_index_unmap(struct mailbox_list_index *index) { - if (index->mail_index->nfs_flush && index->fd != -1) { - nfs_flush_read_cache(index->filepath, index->fd, - F_UNLCK, FALSE); - } - if (index->file_cache != NULL) file_cache_invalidate(index->file_cache, 0, (uoff_t)-1); @@ -229,16 +224,18 @@ return 1; if (index->mail_index->nfs_flush) - nfs_flush_attr_cache(index->filepath); + nfs_flush_attr_cache_unlocked(index->filepath); if (nfs_safe_stat(index->filepath, &st1) < 0) { - if (errno == ENOENT) + if (errno == ENOENT || errno == ESTALE) return 1; mailbox_list_index_set_syscall_error(index, "stat()"); return -1; } if (fstat(index->fd, &st2) < 0) { + if (errno == ESTALE) + return 1; mailbox_list_index_set_syscall_error(index, "fstat()"); return -1; }
--- a/src/lib-storage/index/maildir/maildir-keywords.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-storage/index/maildir/maildir-keywords.c Sun Nov 18 08:40:59 2007 +0200 @@ -118,7 +118,7 @@ errors. */ if ((mk->storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) - nfs_flush_attr_cache(mk->path); + nfs_flush_attr_cache_unlocked(mk->path); if (nfs_safe_stat(mk->path, &st) < 0) { if (errno == ENOENT) {
--- a/src/lib-storage/index/maildir/maildir-uidlist.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-storage/index/maildir/maildir-uidlist.c Sun Nov 18 08:40:59 2007 +0200 @@ -653,9 +653,14 @@ /* NFS: either the file hasn't been changed, or it has already been deleted and the inodes just happen to be the same. check if the fd is still valid. */ - if (!nfs_flush_attr_cache_fd(uidlist->path, uidlist->fd)) { - *recreated_r = TRUE; - return 1; + if (fstat(uidlist->fd, &st) < 0) { + if (errno == ESTALE) { + *recreated_r = TRUE; + return 1; + } + mail_storage_set_critical(storage, + "fstat(%s) failed: %m", uidlist->path); + return -1; } } @@ -682,7 +687,7 @@ if (!nfs_flush) uidlist->nfs_dirty_refresh = TRUE; else { - nfs_flush_attr_cache(uidlist->path); + nfs_flush_attr_cache_unlocked(uidlist->path); uidlist->nfs_dirty_refresh = FALSE; } } @@ -704,7 +709,6 @@ break; /* ESTALE - try reopening and rereading */ maildir_uidlist_close(uidlist); - nfs_flush_attr_cache(uidlist->path); } if (ret >= 0) uidlist->initial_read = TRUE;
--- a/src/lib-storage/index/mbox/mbox-lock.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-storage/index/mbox/mbox-lock.c Sun Nov 18 08:40:59 2007 +0200 @@ -173,7 +173,7 @@ if (mbox->mbox_fd != -1) { if ((mbox->storage->storage.flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) - nfs_flush_attr_cache(mbox->path); + nfs_flush_attr_cache_maybe_locked(mbox->path); if (nfs_safe_stat(mbox->path, &st) < 0) { mbox_set_syscall_error(mbox, "stat()"); return -1; @@ -567,9 +567,13 @@ if ((mbox->storage->storage.flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) { - nfs_flush_read_cache(mbox->path, mbox->mbox_fd, - fcntl_locked ? lock_type : F_UNLCK, - fcntl_locked); + if (fcntl_locked) { + nfs_flush_read_cache_locked(mbox->path, + mbox->mbox_fd); + } else { + nfs_flush_read_cache_unlocked(mbox->path, + mbox->mbox_fd); + } } mbox->mbox_lock_id += 2;
--- a/src/lib-storage/mailbox-list.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib-storage/mailbox-list.c Sun Nov 18 08:40:59 2007 +0200 @@ -242,6 +242,7 @@ if (list->file_create_mode != (mode_t)-1) { *mode_r = list->file_create_mode; *gid_r = list->file_create_gid; + return; } path = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
--- a/src/lib/file-dotlock.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib/file-dotlock.c Sun Nov 18 08:40:59 2007 +0200 @@ -159,7 +159,7 @@ /* don't waste time flushing attribute cache the first time we're here. if it's stale we'll get back here soon. */ if (lock_info->set->nfs_flush && lock_info->lock_stated) - nfs_flush_attr_cache(lock_info->lock_path); + nfs_flush_attr_cache_unlocked(lock_info->lock_path); lock_info->lock_stated = TRUE; if (nfs_safe_lstat(lock_info->lock_path, &st) < 0) { @@ -249,7 +249,7 @@ /* possibly stale lock file. check also the timestamp of the file we're protecting. */ if (lock_info->set->nfs_flush) - nfs_flush_attr_cache(lock_info->path); + nfs_flush_attr_cache_maybe_locked(lock_info->path); if (nfs_safe_stat(lock_info->path, &st) < 0) { if (errno == ENOENT) { /* file doesn't exist. treat it as if
--- a/src/lib/nfs-workarounds.c Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib/nfs-workarounds.c Sun Nov 18 08:40:59 2007 +0200 @@ -34,10 +34,9 @@ #include <unistd.h> #include <sys/stat.h> -#ifdef __linux__ +#if defined (__linux__) || defined(__sun) # define READ_CACHE_FLUSH_FCNTL #endif - #if defined(__FreeBSD__) || defined(__sun) # define ATTRCACHE_FLUSH_CHOWN_UID_1 #endif @@ -67,7 +66,7 @@ break; dir = t_strdup_until(path, dir); } - nfs_flush_attr_cache(path); + nfs_flush_attr_cache_dir(path); if (stat(dir, &st) < 0) { /* maybe it's gone or something else bad happened to it. in any case we can't open the file, so fail @@ -163,38 +162,6 @@ return 0; } -static bool nfs_flush_fchown_uid(const char *path, int fd) -{ - struct stat st; - uid_t uid; - - if (fstat(fd, &st) < 0) { - if (likely(errno == ESTALE)) { - /* ESTALE causes the OS to flush the attr cache */ - return FALSE; - } - i_error("nfs_flush_fchown_uid: fstat(%s) failed: %m", path); - return TRUE; - } -#ifdef ATTRCACHE_FLUSH_CHOWN_UID_1 - uid = (uid_t)-1; -#else - uid = st.st_uid; -#endif - if (fchown(fd, uid, (gid_t)-1) < 0) { - if (errno == ESTALE) - return FALSE; - if (likely(errno == EACCES || errno == EPERM)) { - /* attr cache is flushed */ - return TRUE; - } - - i_error("nfs_flush_fchown_uid: fchown(%s) failed: %m", path); - } - return TRUE; -} - -#ifndef __FreeBSD__ static void nfs_flush_chown_uid(const char *path) { uid_t uid; @@ -211,7 +178,7 @@ /* ESTALE causes the OS to flush the attr cache */ return; } - if (unlikely(errno != ENOENT)) { + if (errno != ENOENT) { i_error("nfs_flush_chown_uid: stat(%s) failed: %m", path); return; @@ -225,18 +192,17 @@ } #endif if (chown(path, uid, (gid_t)-1) < 0) { - if (likely(errno == ESTALE || errno == EACCES || - errno == EPERM || errno == ENOENT)) { + if (errno == ESTALE || errno == EACCES || + errno == EPERM || errno == ENOENT) { /* attr cache is flushed */ return; } i_error("nfs_flush_chown_uid: chown(%s) failed: %m", path); } } -#endif #ifdef READ_CACHE_FLUSH_FCNTL -static void nfs_flush_fcntl(const char *path, int fd, int old_lock_type) +static void nfs_flush_fcntl(const char *path, int fd) { struct flock fl; int ret; @@ -245,7 +211,7 @@ again. It should succeed just fine. If was was unlocked, we'll have to get a lock and then unlock it. Linux 2.6 flushes read cache only when read/write locking succeeded. */ - fl.l_type = old_lock_type != F_UNLCK ? old_lock_type : F_RDLCK; + fl.l_type = F_RDLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; @@ -259,14 +225,77 @@ return; } - if (old_lock_type == F_UNLCK) { - fl.l_type = F_UNLCK; - (void)fcntl(fd, F_SETLKW, &fl); - } + fl.l_type = F_UNLCK; + (void)fcntl(fd, F_SETLKW, &fl); } #endif -static void nfs_flush_attr_cache_dir(const char *path) +static void nfs_flush_attr_cache_parent_dir(const char *path) +{ + const char *p; + + p = strrchr(path, '/'); + if (p == NULL) + nfs_flush_attr_cache_dir("."); + else { + t_push(); + nfs_flush_attr_cache_dir(t_strdup_until(path, p)); + t_pop(); + } +} + +void nfs_flush_attr_cache_unlocked(const char *path) +{ +#ifndef __FreeBSD__ + int fd; + + fd = open(path, O_RDONLY); + if (fd != -1) + (void)close(fd); + else if (errno == ESTALE) { + /* this already flushed the cache */ + } else +#endif + { + /* most likely ENOENT, which means a negative cache hit. + flush the parent directory's attribute cache. */ + nfs_flush_attr_cache_parent_dir(path); + } +} + +void nfs_flush_attr_cache_maybe_locked(const char *path) +{ + /* first we'll flush the parent directory to get the file handle + cache flushed */ + nfs_flush_attr_cache_parent_dir(path); + + /* after we now know the latest file handle, flush its attribute + cache */ + nfs_flush_chown_uid(path); +} + +bool nfs_flush_attr_cache_fd_locked(const char *path ATTR_UNUSED, + int fd ATTR_UNUSED) +{ +#ifdef __FreeBSD__ + if (fchown(fd, (uid_t)-1, (gid_t)-1) < 0) { + if (errno == ESTALE) + return FALSE; + if (likely(errno == EACCES || errno == EPERM)) { + /* attr cache is flushed */ + return TRUE; + } + + i_error("nfs_flush_attr_cache_fd_locked: fchown(%s) failed: %m", + path); + } + return TRUE; +#else + return TRUE; +#endif +} + +void nfs_flush_attr_cache_dir(const char *path) { #ifdef __FreeBSD__ /* Unfortunately rmdir() seems to be the only way to flush a @@ -286,38 +315,31 @@ i_error("nfs_flush_dir: rmdir(%s) failed: %m", path); } #else - nfs_flush_chown_uid(path); + int fd; + + fd = open(path, O_RDONLY); + if (fd != -1) + (void)close(fd); #endif } -void nfs_flush_attr_cache(const char *path) +void nfs_flush_read_cache_locked(const char *path ATTR_UNUSED, + int fd ATTR_UNUSED) { - const char *p; - - p = strrchr(path, '/'); - if (p == NULL) - nfs_flush_attr_cache_dir("."); - else { - t_push(); - nfs_flush_attr_cache_dir(t_strdup_until(path, p)); - t_pop(); - } +#ifdef READ_CACHE_FLUSH_FCNTL + /* already flushed when fcntl() was called */ +#else + /* we can only hope that underlying filesystem uses micro/nanosecond + resolution so that attribute cache flushing notices mtime changes */ + nfs_flush_attr_cache_fd_locked(path, fd); +#endif } -bool nfs_flush_attr_cache_fd(const char *path, int fd) -{ - return nfs_flush_fchown_uid(path, fd); -} - -void nfs_flush_read_cache(const char *path, int fd, - int lock_type ATTR_UNUSED, - bool just_locked ATTR_UNUSED) +void nfs_flush_read_cache_unlocked(const char *path, int fd) { #ifdef READ_CACHE_FLUSH_FCNTL - if (!just_locked) - nfs_flush_fcntl(path, fd, lock_type); + nfs_flush_fcntl(path, fd); #else - /* FreeBSD, Solaris */ - nfs_flush_fchown_uid(path, fd); + nfs_flush_read_cache_locked(path, fd); #endif }
--- a/src/lib/nfs-workarounds.h Sun Nov 18 08:01:54 2007 +0200 +++ b/src/lib/nfs-workarounds.h Sun Nov 18 08:40:59 2007 +0200 @@ -19,18 +19,22 @@ is 1, otherwise stat() first to find it out. */ int nfs_safe_link(const char *oldpath, const char *newpath, bool links1); -/* Flush attribute cache for given path. This actually flushes the parent - directory's attribute cache to make sure that the file handle also gets - refreshed. */ -void nfs_flush_attr_cache(const char *path); -/* Flush attribute cache for given file descriptor. +/* Flush attribute cache for given path. The file must not be fcntl locked or + the locks may get dropped. */ +void nfs_flush_attr_cache_unlocked(const char *path); +/* Flush attribute cache for given path. The file may be fcntl locked. */ +void nfs_flush_attr_cache_maybe_locked(const char *path); +/* Flush attribute cache for a fcntl locked file descriptor. If locking flushes + the attribute cache with the running OS, this function does nothing. The given path is used only for logging. */ -bool nfs_flush_attr_cache_fd(const char *path, int fd); -/* Flush read cache for given fd. lock_type must be set to the file's current - fcntl locking state (F_UNLCK, F_RDLCK, F_WRLCK). Set just_locked=TRUE if the - file was locked at the same time as read cache flush was wanted (to avoid - re-locking the file unneededly). */ -void nfs_flush_read_cache(const char *path, int fd, - int lock_type, bool just_locked); +bool nfs_flush_attr_cache_fd_locked(const char *path, int fd); +/* Flush a directory's attribute cache. */ +void nfs_flush_attr_cache_dir(const char *path); + +/* Flush read cache for fd that was just fcntl locked. If the OS flushes + read cache when fcntl locking file, this function does nothing. */ +void nfs_flush_read_cache_locked(const char *path, int fd); +/* Flush read cache for fd that doesn't have fcntl locks. */ +void nfs_flush_read_cache_unlocked(const char *path, int fd); #endif