diff src/lib-index/mail-transaction-log.c @ 4023:b19ccbaa3802 HEAD

Try to handle ESTALE NFS errors the best way we can.
author Timo Sirainen <timo.sirainen@movial.fi>
date Thu, 16 Feb 2006 17:23:00 +0200
parents 0d64f8888dcd
children cd3d26cf124a
line wrap: on
line diff
--- a/src/lib-index/mail-transaction-log.c	Thu Feb 16 16:58:31 2006 +0200
+++ b/src/lib-index/mail-transaction-log.c	Thu Feb 16 17:23:00 2006 +0200
@@ -4,6 +4,8 @@
 #include "ioloop.h"
 #include "buffer.h"
 #include "file-dotlock.h"
+#include "safe-open.h"
+#include "close-keep-errno.h"
 #include "read-full.h"
 #include "write-full.h"
 #include "mmap-util.h"
@@ -278,6 +280,8 @@
 static void
 mail_transaction_log_file_free(struct mail_transaction_log_file *file)
 {
+        int old_errno = errno;
+
 	mail_transaction_log_file_unlock(file);
 
 	if (file == file->log->head)
@@ -305,7 +309,9 @@
 	}
 
 	i_free(file->filepath);
-	i_free(file);
+        i_free(file);
+
+        errno = old_errno;
 }
 
 int mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
@@ -357,10 +363,11 @@
 
 	ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
 	if (ret < 0) {
-		// FIXME: handle ESTALE
-		mail_index_file_set_syscall_error(file->log->index,
-						  file->filepath,
-						  "pread_full()");
+                if (errno != ESTALE) {
+                        mail_index_file_set_syscall_error(file->log->index,
+                                                          file->filepath,
+                                                          "pread_full()");
+                }
 		return -1;
 	}
 	if (ret == 0) {
@@ -478,7 +485,7 @@
 
 static int
 mail_transaction_log_file_create2(struct mail_transaction_log *log,
-				  const char *path, int fd,
+				  const char *path, int new_fd,
 				  struct dotlock **dotlock,
 				  dev_t dev, ino_t ino, uoff_t file_size)
 {
@@ -486,28 +493,31 @@
 	struct mail_transaction_log_header hdr;
 	struct stat st;
 	const char *path2;
-	int fd2, ret;
+	int old_fd, ret;
 	bool found;
 
 	/* log creation is locked now - see if someone already created it */
-	fd2 = open(path, O_RDWR);
-	if (fd2 != -1) {
-		if ((ret = fstat(fd2, &st)) < 0) {
-			mail_index_file_set_syscall_error(index, path,
-							  "fstat()");
+	old_fd = safe_open(path, O_RDWR);
+	if (old_fd != -1) {
+		if ((ret = fstat(old_fd, &st)) < 0) {
+                        mail_index_file_set_syscall_error(index, path,
+                                                          "fstat()");
 		} else if (st.st_ino == ino && CMP_DEV_T(st.st_dev, dev) &&
 			   (uoff_t)st.st_size == file_size) {
 			/* same file, still broken */
 		} else {
-			(void)file_dotlock_delete(dotlock);
-			return fd2;
+                        /* file changed, use the new file */
+                        (void)file_dotlock_delete(dotlock);
+			return old_fd;
 		}
 
-		(void)close(fd2);
-		fd2 = -1;
+		(void)close(old_fd);
+		old_fd = -1;
 
-		if (ret < 0)
-			return -1;
+                if (ret < 0) {
+                        /* fstat() failure, return after closing fd.. */
+                        return -1;
+                }
 		found = TRUE;
 	} else if (errno != ENOENT) {
 		mail_index_file_set_syscall_error(index, path, "open()");
@@ -519,7 +529,7 @@
 	if (mail_transaction_log_init_hdr(log, &hdr) < 0)
 		return -1;
 
-	if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
+	if (write_full(new_fd, &hdr, sizeof(hdr)) < 0) {
 		mail_index_file_set_syscall_error(index, path,
 						  "write_full()");
 		return -1;
@@ -529,7 +539,8 @@
 	if (found) {
 		path2 = t_strconcat(path, ".2", NULL);
 		if (rename(path, path2) < 0) {
-			i_error("rename(%s, %s) failed: %m", path, path2);
+                        mail_index_set_error(index,
+                        	"rename(%s, %s) failed: %m", path, path2);
 			/* ignore the error. we don't care that much about the
 			   second log file and we're going to overwrite this
 			   first one. */
@@ -541,7 +552,7 @@
 		return -1;
 
 	/* success */
-	return fd;
+	return new_fd;
 }
 
 static int
@@ -551,7 +562,7 @@
 {
 	struct dotlock *dotlock;
         mode_t old_mask;
-	int fd, fd2;
+	int fd;
 
 	i_assert(!MAIL_INDEX_IS_IN_MEMORY(log->index));
 
@@ -574,14 +585,16 @@
 		return -1;
 	}
 
-	fd2 = mail_transaction_log_file_create2(log, path, fd, &dotlock,
-						dev, ino, file_size);
-	if (fd2 < 0) {
+        /* either fd gets used or the dotlock gets deleted and returned fd
+           is for the existing file */
+        fd = mail_transaction_log_file_create2(log, path, fd, &dotlock,
+                                               dev, ino, file_size);
+	if (fd < 0) {
 		if (dotlock != NULL)
 			(void)file_dotlock_delete(&dotlock);
 		return -1;
 	}
-	return fd2;
+	return fd;
 }
 
 static void
@@ -640,8 +653,11 @@
 	*file_r = NULL;
 
 	if (fstat(fd, &st) < 0) {
-		mail_index_file_set_syscall_error(log->index, path, "fstat()");
-		(void)close(fd);
+                if (errno != ESTALE) {
+                        mail_index_file_set_syscall_error(log->index, path,
+                                                          "fstat()");
+                }
+		close_keep_errno(fd);
 		return -1;
 	}
 
@@ -668,15 +684,20 @@
 
 static struct mail_transaction_log_file *
 mail_transaction_log_file_fd_open_or_create(struct mail_transaction_log *log,
-					    const char *path, int fd)
+                                            const char *path, int fd,
+                                            bool *retry_r)
 {
 	struct mail_transaction_log_file *file;
 	struct stat st;
 	int ret;
 
+        *retry_r = FALSE;
+
 	ret = mail_transaction_log_file_fd_open(log, &file, path, fd, TRUE);
-	if (ret < 0)
-		return NULL;
+        if (ret < 0) {
+                *retry_r = errno == ESTALE;
+                return NULL;
+        }
 
 	if (ret == 0) {
 		/* corrupted header */
@@ -686,7 +707,7 @@
 		if (fd == -1)
 			ret = -1;
 		else if (fstat(fd, &st) < 0) {
-			mail_index_file_set_syscall_error(log->index, path,
+                        mail_index_file_set_syscall_error(log->index, path,
 							  "fstat()");
 			(void)close(fd);
 			fd = -1;
@@ -707,6 +728,7 @@
 		}
 	}
 	if (ret <= 0) {
+                *retry_r = errno == ESTALE;
 		mail_transaction_log_file_free(file);
 		return NULL;
 	}
@@ -742,25 +764,39 @@
 mail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
 					 const char *path)
 {
-	int fd;
+        struct mail_transaction_log_file *file;
+        unsigned int i;
+        bool retry;
+        int fd;
 
 	if (MAIL_INDEX_IS_IN_MEMORY(log->index))
 		return mail_transaction_log_file_alloc_in_memory(log);
 
-	fd = open(path, O_RDWR);
-	if (fd == -1) {
-		if (errno != ENOENT) {
-			mail_index_file_set_syscall_error(log->index, path,
-							  "open()");
-			return NULL;
-		}
+        for (i = 0; ; i++) {
+                fd = safe_open(path, O_RDWR);
+                if (fd == -1) {
+                        if (errno != ENOENT) {
+                                mail_index_file_set_syscall_error(log->index,
+                                                                  path,
+                                                                  "open()");
+                                return NULL;
+                        }
 
-		fd = mail_transaction_log_file_create(log, path, 0, 0, 0);
-		if (fd == -1)
-			return NULL;
-	}
+                        /* doesn't exist, try creating it */
+                        fd = mail_transaction_log_file_create(log, path,
+                                                              0, 0, 0);
+                        if (fd == -1)
+                                return NULL;
+                }
 
-	return mail_transaction_log_file_fd_open_or_create(log, path, fd);
+                file = mail_transaction_log_file_fd_open_or_create(log, path,
+                                                                   fd, &retry);
+                if (file != NULL || !retry ||
+                    i == MAIL_INDEX_ESTALE_RETRY_COUNT)
+                        return file;
+
+                /* ESTALE - retry */
+        }
 }
 
 static struct mail_transaction_log_file *
@@ -768,21 +804,34 @@
 			       const char *path)
 {
 	struct mail_transaction_log_file *file;
-	int fd, ret;
+        unsigned int i;
+        int fd, ret;
 
-	fd = open(path, O_RDWR);
-	if (fd == -1) {
-		mail_index_file_set_syscall_error(log->index, path, "open()");
-		return NULL;
-	}
+        for (i = 0;; i++) {
+                fd = safe_open(path, O_RDWR);
+                if (fd == -1) {
+                        mail_index_file_set_syscall_error(log->index, path,
+                                                          "open()");
+                        return NULL;
+                }
 
-	ret = mail_transaction_log_file_fd_open(log, &file, path, fd, TRUE);
-	if (ret <= 0) {
-		/* error / corrupted */
-		if (ret == 0)
-			mail_transaction_log_file_free(file);
-		return NULL;
-	}
+                ret = mail_transaction_log_file_fd_open(log, &file, path,
+                                                        fd, TRUE);
+                if (ret > 0)
+                        break;
+
+                /* error / corrupted */
+                if (ret == 0)
+                        mail_transaction_log_file_free(file);
+                else {
+                        if (errno == ESTALE &&
+                            i < MAIL_INDEX_ESTALE_RETRY_COUNT) {
+                                /* try again */
+                                continue;
+                        }
+                }
+                return NULL;
+        }
 
 	mail_transaction_log_file_add_to_head(file);
 	return file;
@@ -807,7 +856,8 @@
 {
 	struct mail_transaction_log_file *file;
 	const char *path = log->head->filepath;
-	struct stat st;
+        struct stat st;
+        bool retry;
 	int fd;
 
 	i_assert(log->head->locked);
@@ -815,6 +865,8 @@
 	if (MAIL_INDEX_IS_IN_MEMORY(log->index))
 		file = mail_transaction_log_file_alloc_in_memory(log);
 	else {
+                /* we're locked, we shouldn't need to worry about ESTALE
+                   problems in here. */
 		if (fstat(log->head->fd, &st) < 0) {
 			mail_index_file_set_syscall_error(log->index, path,
 							  "fstat()");
@@ -827,10 +879,16 @@
 		if (fd == -1)
 			return -1;
 
-		file = mail_transaction_log_file_fd_open_or_create(log,
-								   path, fd);
-		if (file == NULL)
-			return -1;
+                file = mail_transaction_log_file_fd_open_or_create(log, path,
+                                                                   fd, &retry);
+                if (file == NULL) {
+                        if (retry) {
+                                mail_index_set_error(log->index,
+                                	"%s: ESTALE unexpected while locked",
+                                        path);
+                        }
+                        return -1;
+                }
 	}
 
 	if (lock) {
@@ -920,7 +978,7 @@
 	/* see if we have it in log.2 file */
 	path = t_strconcat(log->index->filepath,
 			   MAIL_TRANSACTION_LOG_SUFFIX".2", NULL);
-	fd = open(path, O_RDWR);
+	fd = safe_open(path, O_RDWR);
 	if (fd == -1) {
 		if (errno == ENOENT)
 			return 0;
@@ -930,7 +988,12 @@
 	}
 
 	if (fstat(fd, &st) < 0) {
-		mail_index_file_set_syscall_error(log->index, path, "fstat()");
+                if (errno == ESTALE) {
+                        /* treat as "doesn't exist" */
+                        (void)close(fd);
+                        return 0;
+                }
+                mail_index_file_set_syscall_error(log->index, path, "fstat()");
 		return -1;
 	}
 
@@ -954,7 +1017,11 @@
 			}
 			mail_transaction_log_file_free(file);
 			return 0;
-		}
+                }
+                if (errno == ESTALE) {
+                        /* treat as "doesn't exist" */
+                        return 0;
+                }
 		return -1;
 	}