dovecot: Added nfs_flush_attr_cache() and nfs_flush_read_cache().
dovecot at dovecot.org
dovecot at dovecot.org
Fri Jul 13 00:18:31 EEST 2007
details: http://hg.dovecot.org/dovecot/rev/a290b84d144a
changeset: 5970:a290b84d144a
user: Timo Sirainen <tss at iki.fi>
date: Thu Jul 12 23:54:13 2007 +0300
description:
Added nfs_flush_attr_cache() and nfs_flush_read_cache().
diffstat:
2 files changed, 154 insertions(+), 3 deletions(-)
src/lib/nfs-workarounds.c | 140 ++++++++++++++++++++++++++++++++++++++++++++-
src/lib/nfs-workarounds.h | 17 ++++-
diffs (187 lines):
diff -r 29770d8a013b -r a290b84d144a src/lib/nfs-workarounds.c
--- a/src/lib/nfs-workarounds.c Thu Jul 12 23:52:03 2007 +0300
+++ b/src/lib/nfs-workarounds.c Thu Jul 12 23:54:13 2007 +0300
@@ -1,4 +1,31 @@
-/* Copyright (c) 2006 Timo Sirainen */
+/* Copyright (c) 2006-2007 Timo Sirainen */
+
+/*
+ These tests were done with various Linux 2.6 kernels, FreeBSD 6.2 and
+ Solaris 8 and 10.
+
+ Attribute cache is usually flushed with chown()ing or fchown()ing the file.
+ The safest way would be to use uid=-1 gid=-1, but this doesn't work with
+ Linux (it does with FreeBSD 6.2 and Solaris). So we'll first get the
+ file's owner and use it. As long as we're not root the file's owner can't
+ change accidentally. If would be possible to also use chmod()/fchmod(), but
+ that's riskier since it could actually cause an unwanted change.
+
+ Write cache can be flushed with fdatasync(). It's all we need, but other
+ tested alternatives are: fcntl locking (Linux 2.6, Solaris),
+ fchown() (Solaris) and dup()+close() (Linux 2.6, Solaris).
+
+ Read cache flushing is more problematic. There's no universal way to do it.
+ The working methods are:
+
+ Linux 2.6: fcntl(), O_DIRECT
+ Solaris: fchown(), fcntl(), dup()+close()
+ FreeBSD 6.2: fchown()
+
+ fchown() can be easily used for Solaris and FreeBSD, but Linux requires
+ playing with locks. O_DIRECT requires CONFIG_NFS_DIRECTIO to be enabled, so
+ we can't always use it.
+*/
#include "lib.h"
#include "nfs-workarounds.h"
@@ -6,6 +33,10 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
+
+#ifdef __linux__
+# define READ_CACHE_FLUSH_FCNTL
+#endif
static int
nfs_safe_do(const char *path, int (*callback)(const char *path, void *context),
@@ -96,3 +127,110 @@ int nfs_safe_lstat(const char *path, str
{
return nfs_safe_do(path, nfs_safe_lstat_callback, buf);
}
+
+static void nfs_flush_fchown_uid(const char *path, int fd)
+{
+ struct stat st;
+
+ if (fstat(fd, &st) < 0) {
+ if (errno == ESTALE) {
+ /* ESTALE causes the OS to flush the attr cache */
+ return;
+ }
+ i_error("nfs_flush_fchown_uid: fstat(%s) failed: %m", path);
+ return;
+ }
+ if (fchown(fd, st.st_uid, (gid_t)-1) < 0) {
+ if (errno == ESTALE || errno == EACCES || errno == EPERM) {
+ /* attr cache is flushed */
+ return;
+ }
+
+ i_error("nfs_flush_fchown_uid: fchown(%s) failed: %m", path);
+ }
+}
+
+static void nfs_flush_chown_uid(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) < 0) {
+ if (errno == ESTALE) {
+ /* ESTALE causes the OS to flush the attr cache */
+ return;
+ }
+ if (errno != ENOENT) {
+ i_error("nfs_flush_chown_uid: stat(%s) failed: %m",
+ path);
+ return;
+ }
+
+ /* flush a negative cache entry. use effective UID to chown.
+ it probably doesn't really matter what UID is used, because
+ as long as we're not root we don't have permission to really
+ change it anyway */
+ st.st_uid = geteuid();
+ }
+ if (chown(path, st.st_uid, (gid_t)-1) < 0) {
+ 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);
+ }
+}
+
+#ifdef READ_CACHE_FLUSH_FCNTL
+static void nfs_flush_fcntl(const char *path, int fd, int old_lock_type)
+{
+ struct flock fl;
+ int ret;
+
+ /* If the file was already locked, we'll just get the same lock
+ 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_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ alarm(60);
+ ret = fcntl(fd, F_SETLKW, &fl);
+ alarm(0);
+
+ if (ret < 0) {
+ i_error("nfs_flush_fcntl: fcntl(%s, F_RDLCK) failed: %m", path);
+ return;
+ }
+
+ if (old_lock_type == F_UNLCK) {
+ fl.l_type = F_UNLCK;
+ (void)fcntl(fd, F_SETLKW, &fl);
+ }
+}
+#endif
+
+void nfs_flush_attr_cache(const char *path)
+{
+ nfs_flush_chown_uid(path);
+}
+
+void nfs_flush_attr_cache_fd(const char *path, int fd)
+{
+ 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__)
+{
+#ifdef READ_CACHE_FLUSH_FCNTL
+ if (!just_locked)
+ nfs_flush_fcntl(path, fd, lock_type);
+#else
+ /* FreeBSD, Solaris */
+ nfs_flush_fchown_uid(path, fd);
+#endif
+}
diff -r 29770d8a013b -r a290b84d144a src/lib/nfs-workarounds.h
--- a/src/lib/nfs-workarounds.h Thu Jul 12 23:52:03 2007 +0300
+++ b/src/lib/nfs-workarounds.h Thu Jul 12 23:54:13 2007 +0300
@@ -8,10 +8,23 @@
file and retrying the operation. */
#define NFS_ESTALE_RETRY_COUNT 10
-/* open() with some NFS workarounds */
+/* Same as open(), but try to handle ESTALE errors. */
int nfs_safe_open(const char *path, int flags);
-/* stat() with some NFS workarounds */
+/* Same as stat(), but try to handle ESTALE errors.
+ Doesn't flush attribute cache. */
int nfs_safe_stat(const char *path, struct stat *buf);
int nfs_safe_lstat(const char *path, struct stat *buf);
+/* Flush attribute cache for given path */
+void nfs_flush_attr_cache(const char *path);
+/* Flush attribute cache for given file descriptor.
+ The given path is used only for logging. */
+void 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);
+
#endif
More information about the dovecot-cvs
mailing list