nfs-workarounds.c revision 76b43e4417bab52e913da39b5f5bc2a130d3f149
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen These tests were done with various Linux 2.6 kernels, FreeBSD 6.2 and
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen Solaris 8 and 10.
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen Attribute cache is usually flushed with chown()ing or fchown()ing the file.
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen The safest way would be to use uid=-1 gid=-1, but this doesn't work with
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen Linux (it does with FreeBSD 6.2 and Solaris). So we'll first get the
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen file's owner and use it. As long as we're not root the file's owner can't
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen change accidentally. If would be possible to also use chmod()/fchmod(), but
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen that's riskier since it could actually cause an unwanted change.
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen Write cache can be flushed with fdatasync(). It's all we need, but other
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen tested alternatives are: fcntl locking (Linux 2.6, Solaris),
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen fchown() (Solaris) and dup()+close() (Linux 2.6, Solaris).
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen Read cache flushing is more problematic. There's no universal way to do it.
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen The working methods are:
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen Linux 2.6: fcntl(), O_DIRECT
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen Solaris: fchown(), fcntl(), dup()+close()
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen FreeBSD 6.2: fchown()
701eb90460d6c57845dc4e0bf595a5d0b90b01c1Timo Sirainen fchown() can be easily used for Solaris and FreeBSD, but Linux requires
701eb90460d6c57845dc4e0bf595a5d0b90b01c1Timo Sirainen playing with locks. O_DIRECT requires CONFIG_NFS_DIRECTIO to be enabled, so
75a7ba70c7b377eff0f7124b8943cf2976ac2533Aki Tuomi we can't always use it.
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainenstatic void nfs_flush_file_handle_cache_parent_dir(const char *path);
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainennfs_safe_do(const char *path, int (*callback)(const char *path, void *context),
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen unsigned int i;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen for (i = 1;; i++) {
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen if (ret == 0 || errno != ESTALE || i == NFS_ESTALE_RETRY_COUNT)
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen /* ESTALE: Some operating systems may fail with this if they
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen can't internally revalidate the NFS file handle. Flush the
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen file handle and try again */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenstatic int nfs_safe_open_callback(const char *path, void *context)
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen if (nfs_safe_do(path, nfs_safe_open_callback, &ctx) < 0)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic int nfs_safe_stat_callback(const char *path, void *context)
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenint nfs_safe_stat(const char *path, struct stat *buf)
c4478af52de63804efef2055580adf1dfc8679c6Timo Sirainen return nfs_safe_do(path, nfs_safe_stat_callback, buf);
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainenstatic int nfs_safe_lstat_callback(const char *path, void *context)
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainenint nfs_safe_lstat(const char *path, struct stat *buf)
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen return nfs_safe_do(path, nfs_safe_lstat_callback, buf);
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainenint nfs_safe_link(const char *oldpath, const char *newpath, bool links1)
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen /* FreeBSD at least up to v6.2 converts EEXIST errors to
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen /* We don't know if it succeeded or failed. stat() to make sure. */
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenstatic void nfs_flush_chown_uid(const char *path)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen /* ESTALE causes the OS to flush the attr cache */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen i_error("nfs_flush_chown_uid: stat(%s) failed: %m", path);
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen if (errno == ESTALE || errno == EPERM || errno == ENOENT) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* attr cache is flushed */
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen i_error("nfs_flush_chown_uid: chown(%s) failed: %m", path);
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainenstatic bool nfs_flush_fchown_uid(const char *path, int fd)
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen i_error("nfs_flush_attr_cache_fchown: fstat(%s) failed: %m",
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (likely(errno == EACCES || errno == EPERM)) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* attr cache is flushed */
aef407f147034a569591c0f59593342a8c7b39eaTimo Sirainen i_error("nfs_flush_attr_cache_fd_locked: fchown(%s) failed: %m",
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainenstatic void nfs_flush_fcntl(const char *path, int fd)
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen /* If the file was already locked, we'll just get the same lock
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen again. It should succeed just fine. If was was unlocked, we'll
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen have to get a lock and then unlock it. Linux 2.6 flushes read cache
c2a66e7950cb4d3fc4d68e4480ea8f39bdd7c871Timo Sirainen only when read/write locking succeeded. */
c2a66e7950cb4d3fc4d68e4480ea8f39bdd7c871Timo Sirainen i_error("nfs_flush_fcntl: fcntl(%s, F_RDLCK) failed: %m", path);
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenvoid nfs_flush_attr_cache_unlocked(const char *path)
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen /* Try to flush the attribute cache the nice way first. */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* this already flushed the cache */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* most likely ENOENT, which means a negative cache hit.
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen flush the file handles for its parent directory. */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenvoid nfs_flush_attr_cache_maybe_locked(const char *path)
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainenbool nfs_flush_attr_cache_fd_locked(const char *path ATTR_UNUSED,
2be7df5df08ac4639ad83559ec5fcf552c84fb4aTimo Sirainen /* FreeBSD doesn't flush attribute cache with fcntl(), so we have
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen to do it ourself. */
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen /* Linux and Solaris are fine. */
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainenstatic bool nfs_flush_file_handle_cache_dir(const char *path)
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen /* chown()ing parent is the safest way to handle this */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* rmdir() is the only choice with FreeBSD and Solaris */
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen "rmdir(%s) unexpectedly "
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen "rmdir(%s) unexpectedly "
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen } else if (errno == ESTALE || errno == ENOTDIR ||
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen /* expected failures */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenstatic void nfs_flush_file_handle_cache_parent_dir(const char *path)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen const char *p;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen nfs_flush_file_handle_cache_dir(t_strdup_until(path, p));
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenvoid nfs_flush_file_handle_cache(const char *path)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenvoid nfs_flush_read_cache_locked(const char *path ATTR_UNUSED,
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen /* already flushed when fcntl() was called */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen /* we can only hope that underlying filesystem uses micro/nanosecond
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen resolution so that attribute cache flushing notices mtime changes */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenvoid nfs_flush_read_cache_unlocked(const char *path, int fd)