nfs-workarounds.c revision 76b43e4417bab52e913da39b5f5bc2a130d3f149
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen/*
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen These tests were done with various Linux 2.6 kernels, FreeBSD 6.2 and
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen Solaris 8 and 10.
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
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
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
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen Read cache flushing is more problematic. There's no universal way to do it.
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen The working methods are:
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen Linux 2.6: fcntl(), O_DIRECT
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen Solaris: fchown(), fcntl(), dup()+close()
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen FreeBSD 6.2: fchown()
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
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.
75a7ba70c7b377eff0f7124b8943cf2976ac2533Aki Tuomi*/
75a7ba70c7b377eff0f7124b8943cf2976ac2533Aki Tuomi
75a7ba70c7b377eff0f7124b8943cf2976ac2533Aki Tuomi#include "lib.h"
75a7ba70c7b377eff0f7124b8943cf2976ac2533Aki Tuomi#include "nfs-workarounds.h"
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#include <fcntl.h>
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen#include <unistd.h>
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen#include <sys/stat.h>
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen#if defined (__linux__) || defined(__sun)
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen# define READ_CACHE_FLUSH_FCNTL
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen#endif
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#if defined(__FreeBSD__) || defined(__sun)
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainen# define ATTRCACHE_FLUSH_CHOWN_UID_1
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#endif
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
419cf63077e755935ce105747d6ebc67b7d38a7fTimo Sirainenstatic void nfs_flush_file_handle_cache_parent_dir(const char *path);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenstatic int
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainennfs_safe_do(const char *path, int (*callback)(const char *path, void *context),
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen void *context)
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen{
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen unsigned int i;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen int ret;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen for (i = 1;; i++) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen ret = callback(path, context);
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen if (ret == 0 || errno != ESTALE || i == NFS_ESTALE_RETRY_COUNT)
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen break;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
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 Sirainen nfs_flush_file_handle_cache(path);
20e04227229970d148801c507946666e2a9bd838Timo Sirainen }
20e04227229970d148801c507946666e2a9bd838Timo Sirainen return ret;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen}
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainenstruct nfs_safe_open_context {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen int flags;
39ea5717264668e2c7f9f7986eb821d21785f47fTimo Sirainen int fd;
080a75584cfbe21ffd2d23c6bbb4cd8fdfd0990cTimo Sirainen};
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenstatic int nfs_safe_open_callback(const char *path, void *context)
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen{
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen struct nfs_safe_open_context *ctx = context;
10399559650f552a23949772be79eb6a80198c5aTimo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen ctx->fd = open(path, ctx->flags);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return ctx->fd == -1 ? -1 : 0;
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen}
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainenint nfs_safe_open(const char *path, int flags)
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen{
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen struct nfs_safe_open_context ctx;
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen
39ea5717264668e2c7f9f7986eb821d21785f47fTimo Sirainen i_assert((flags & O_CREAT) == 0);
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen
10399559650f552a23949772be79eb6a80198c5aTimo Sirainen ctx.flags = flags;
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen if (nfs_safe_do(path, nfs_safe_open_callback, &ctx) < 0)
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen return -1;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return ctx.fd;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen}
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic int nfs_safe_stat_callback(const char *path, void *context)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen{
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen struct stat *buf = context;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return stat(path, buf);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen}
c4478af52de63804efef2055580adf1dfc8679c6Timo Sirainen
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenint nfs_safe_stat(const char *path, struct stat *buf)
c4478af52de63804efef2055580adf1dfc8679c6Timo Sirainen{
c4478af52de63804efef2055580adf1dfc8679c6Timo Sirainen return nfs_safe_do(path, nfs_safe_stat_callback, buf);
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen}
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainenstatic int nfs_safe_lstat_callback(const char *path, void *context)
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainen{
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainen struct stat *buf = context;
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainen
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainen return lstat(path, buf);
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainen}
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainen
a5ec9755556e3d97d7e6d78cb1b53046370e6598Timo Sirainenint nfs_safe_lstat(const char *path, struct stat *buf)
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen{
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen return nfs_safe_do(path, nfs_safe_lstat_callback, buf);
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen}
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainenint nfs_safe_link(const char *oldpath, const char *newpath, bool links1)
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen{
f8ead0942a9b7c8fcf91414ed1b534d5807ca555Timo Sirainen struct stat st;
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen nlink_t orig_link_count = 1;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen if (!links1) {
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen if (stat(oldpath, &st) < 0)
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return -1;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen orig_link_count = st.st_nlink;
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen }
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen if (link(oldpath, newpath) == 0) {
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen#ifndef __FreeBSD__
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen return 0;
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen#endif
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen /* FreeBSD at least up to v6.2 converts EEXIST errors to
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen success. */
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen } else if (errno != EEXIST)
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen return -1;
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen /* We don't know if it succeeded or failed. stat() to make sure. */
f990dde096949bd2b76aab28936211689bd6cadcTimo Sirainen if (stat(oldpath, &st) < 0)
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen return -1;
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen if (st.st_nlink == orig_link_count) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen errno = EEXIST;
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen return -1;
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen }
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return 0;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen}
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenstatic void nfs_flush_chown_uid(const char *path)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen{
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen uid_t uid;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen#ifdef ATTRCACHE_FLUSH_CHOWN_UID_1
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen uid = (uid_t)-1;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen#else
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen struct stat st;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen if (stat(path, &st) == 0)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen uid = st.st_uid;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen else {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen if (errno == ESTALE) {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen /* ESTALE causes the OS to flush the attr cache */
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen return;
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen }
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen if (likely(errno == ENOENT)) {
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen nfs_flush_file_handle_cache_parent_dir(path);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen return;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen }
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen i_error("nfs_flush_chown_uid: stat(%s) failed: %m", path);
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen return;
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainen }
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#endif
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen if (chown(path, uid, (gid_t)-1) < 0) {
e3237982a4e6346c2fec4b8f8fb946c826a363fdTimo Sirainen if (errno == ESTALE || errno == EPERM || errno == ENOENT) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* attr cache is flushed */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return;
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen }
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen if (likely(errno == ENOENT)) {
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen nfs_flush_file_handle_cache_parent_dir(path);
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen return;
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen }
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen i_error("nfs_flush_chown_uid: chown(%s) failed: %m", path);
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen }
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen}
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen#ifdef __FreeBSD__
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainenstatic bool nfs_flush_fchown_uid(const char *path, int fd)
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen{
d694b6009574ee6e3cfaee3834cbdbcd431affb0Timo Sirainen uid_t uid;
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen#ifndef ATTRCACHE_FLUSH_CHOWN_UID_1
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen struct stat st;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen if (fstat(fd, &st) < 0) {
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen if (likely(errno == ESTALE))
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen return FALSE;
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen i_error("nfs_flush_attr_cache_fchown: fstat(%s) failed: %m",
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen path);
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen return TRUE;
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen }
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen uid = st.st_uid;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#endif
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (fchown(fd, uid, (gid_t)-1) < 0) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (errno == ESTALE)
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return FALSE;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (likely(errno == EACCES || errno == EPERM)) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* attr cache is flushed */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return TRUE;
aef407f147034a569591c0f59593342a8c7b39eaTimo Sirainen }
aef407f147034a569591c0f59593342a8c7b39eaTimo Sirainen
aef407f147034a569591c0f59593342a8c7b39eaTimo Sirainen i_error("nfs_flush_attr_cache_fd_locked: fchown(%s) failed: %m",
aef407f147034a569591c0f59593342a8c7b39eaTimo Sirainen path);
aef407f147034a569591c0f59593342a8c7b39eaTimo Sirainen }
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen return TRUE;
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen}
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen#endif
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen#ifdef READ_CACHE_FLUSH_FCNTL
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainenstatic void nfs_flush_fcntl(const char *path, int fd)
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen{
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen struct flock fl;
c2a66e7950cb4d3fc4d68e4480ea8f39bdd7c871Timo Sirainen int ret;
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen
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 fl.l_type = F_RDLCK;
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen fl.l_whence = SEEK_SET;
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen fl.l_start = 0;
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen fl.l_len = 0;
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen alarm(60);
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen ret = fcntl(fd, F_SETLKW, &fl);
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen alarm(0);
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen if (unlikely(ret < 0)) {
c2a66e7950cb4d3fc4d68e4480ea8f39bdd7c871Timo Sirainen i_error("nfs_flush_fcntl: fcntl(%s, F_RDLCK) failed: %m", path);
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen return;
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen }
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen fl.l_type = F_UNLCK;
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen (void)fcntl(fd, F_SETLKW, &fl);
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen}
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen#endif
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenvoid nfs_flush_attr_cache_unlocked(const char *path)
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen{
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen int fd;
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen
d3fce898d31ad40b554c91f3035a7f4d7d52ed52Timo Sirainen /* Try to flush the attribute cache the nice way first. */
75a7ba70c7b377eff0f7124b8943cf2976ac2533Aki Tuomi fd = open(path, O_RDONLY);
701eb90460d6c57845dc4e0bf595a5d0b90b01c1Timo Sirainen if (fd != -1)
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainen (void)close(fd);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen else if (errno == ESTALE) {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* this already flushed the cache */
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen } else {
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* most likely ENOENT, which means a negative cache hit.
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen flush the file handles for its parent directory. */
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen nfs_flush_file_handle_cache_parent_dir(path);
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen }
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen}
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainenvoid nfs_flush_attr_cache_maybe_locked(const char *path)
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen{
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen nfs_flush_chown_uid(path);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen}
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainenbool nfs_flush_attr_cache_fd_locked(const char *path ATTR_UNUSED,
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen int fd ATTR_UNUSED)
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen{
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen#ifdef __FreeBSD__
2be7df5df08ac4639ad83559ec5fcf552c84fb4aTimo Sirainen /* FreeBSD doesn't flush attribute cache with fcntl(), so we have
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen to do it ourself. */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen return nfs_flush_fchown_uid(path, fd);
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen#else
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen /* Linux and Solaris are fine. */
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen return TRUE;
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen#endif
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen}
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainenstatic bool nfs_flush_file_handle_cache_dir(const char *path)
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen{
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen#ifdef __linux__
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen /* chown()ing parent is the safest way to handle this */
92d1458b00f4f236c4cec96a696253d3bbf8b05aTimo Sirainen nfs_flush_chown_uid(path);
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen#else
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen /* rmdir() is the only choice with FreeBSD and Solaris */
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen if (unlikely(rmdir(path) == 0)) {
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen if (mkdir(path, 0700) == 0) {
fd1a8db8fa61f9c38f063f62753d1bfef0261e19Timo Sirainen i_warning("nfs_flush_file_handle_cache_dir: "
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen "rmdir(%s) unexpectedly "
52de9d38fcad80df481667bac821cb8222785fe6Timo Sirainen "removed the dir. recreated.", path);
2be7df5df08ac4639ad83559ec5fcf552c84fb4aTimo Sirainen } else {
8f9a18189f01448267100fa54c3b4bb8639a1a56Timo Sirainen i_warning("nfs_flush_file_handle_cache_dir: "
28c75d59f1d1a7caeb85635964f3881c0038eb23Timo Sirainen "rmdir(%s) unexpectedly "
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen "removed the dir. mkdir() failed: %m", path);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen }
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen } else if (errno == ESTALE || errno == ENOTDIR ||
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen errno == ENOTEMPTY || errno == EEXIST) {
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen /* expected failures */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen } else if (errno == ENOENT) {
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen return FALSE;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen } else {
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen i_error("nfs_flush_file_handle_cache_dir: "
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen "rmdir(%s) failed: %m", path);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen }
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#endif
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen return TRUE;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenstatic void nfs_flush_file_handle_cache_parent_dir(const char *path)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen const char *p;
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen p = strrchr(path, '/');
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen if (p == NULL)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen nfs_flush_file_handle_cache_dir(".");
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen else T_FRAME(
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen nfs_flush_file_handle_cache_dir(t_strdup_until(path, p));
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen );
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenvoid nfs_flush_file_handle_cache(const char *path)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen nfs_flush_file_handle_cache_parent_dir(path);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenvoid nfs_flush_read_cache_locked(const char *path ATTR_UNUSED,
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen int fd ATTR_UNUSED)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#ifdef READ_CACHE_FLUSH_FCNTL
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen /* already flushed when fcntl() was called */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#else
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen /* we can only hope that underlying filesystem uses micro/nanosecond
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen resolution so that attribute cache flushing notices mtime changes */
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen nfs_flush_attr_cache_fd_locked(path, fd);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#endif
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainenvoid nfs_flush_read_cache_unlocked(const char *path, int fd)
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen{
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#ifdef READ_CACHE_FLUSH_FCNTL
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen nfs_flush_fcntl(path, fd);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#else
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen nfs_flush_read_cache_locked(path, fd);
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen#endif
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen}
8971ca621b7a7337947306494731b75d1d3919e5Timo Sirainen