]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
NFS-safe subscription file
authorTimo Sirainen <tss@iki.fi>
Sun, 6 Jul 2003 00:29:00 +0000 (03:29 +0300)
committerTimo Sirainen <tss@iki.fi>
Sun, 6 Jul 2003 00:29:00 +0000 (03:29 +0300)
--HG--
branch : HEAD

src/lib-storage/subscription-file/subscription-file.c

index feab48ffeaca80aff83e4dd760a47f508f7c5c06..b295ab12010486aa9adeadec572a9bd8a61d690c 100644 (file)
@@ -3,8 +3,7 @@
 #include "lib.h"
 #include "istream.h"
 #include "ostream.h"
-#include "file-lock.h"
-#include "write-full.h"
+#include "file-dotlock.h"
 #include "mail-storage.h"
 #include "subscription-file.h"
 
@@ -14,6 +13,9 @@
 #define SUBSCRIPTION_FILE_NAME ".subscriptions"
 #define MAX_MAILBOX_LENGTH PATH_MAX
 
+#define SUBSCRIPTION_FILE_LOCK_TIMEOUT 120
+#define SUBSCRIPTION_FILE_STALE_TIMEOUT 30
+
 struct subsfile_list_context {
        pool_t pool;
 
@@ -25,7 +27,7 @@ struct subsfile_list_context {
 };
 
 static int subsfile_set_syscall_error(struct mail_storage *storage,
-                                     const char *path, const char *function)
+                                     const char *function, const char *path)
 {
        i_assert(function != NULL);
 
@@ -35,39 +37,10 @@ static int subsfile_set_syscall_error(struct mail_storage *storage,
        }
 
        mail_storage_set_critical(storage,
-                                 "%s failed with subscription file %s: %m",
-                                 function, path);
+               "%s failed with subscription file %s: %m", function, path);
        return FALSE;
 }
 
-static int subscription_open(struct mail_storage *storage, int update,
-                            const char **path)
-{
-       int fd;
-
-       *path = t_strconcat(storage->dir, "/" SUBSCRIPTION_FILE_NAME, NULL);
-
-       fd = update ? open(*path, O_RDWR | O_CREAT, 0660) :
-               open(*path, O_RDONLY);
-       if (fd == -1) {
-               if (update || errno != ENOENT) {
-                        subsfile_set_syscall_error(storage, "open()", *path);
-                       return -1;
-               }
-
-               return -1;
-       }
-
-       /* FIXME: we should work without locking, rename() would be easiest
-          but .lock would work too */
-       if (file_wait_lock(fd, update ? F_WRLCK : F_RDLCK) <= 0) {
-               subsfile_set_syscall_error(storage, "file_wait_lock()", *path);
-               (void)close(fd);
-               return -1;
-       }
-       return fd;
-}
-
 static const char *next_line(struct mail_storage *storage, const char *path,
                             struct istream *input, int *failed)
 {
@@ -93,84 +66,86 @@ static const char *next_line(struct mail_storage *storage, const char *path,
        return line;
 }
 
-static int stream_cut(struct mail_storage *storage, const char *path,
-                     struct istream *input, uoff_t count)
-{
-       struct ostream *output;
-       int fd, failed;
-
-       fd = i_stream_get_fd(input);
-       i_assert(fd != -1);
-
-       output = o_stream_create_file(fd, default_pool, 4096, FALSE);
-       if (o_stream_seek(output, input->start_offset + input->v_offset) < 0) {
-               failed = TRUE;
-               errno = output->stream_errno;
-               subsfile_set_syscall_error(storage, "o_stream_seek()", path);
-       } else {
-               i_stream_skip(input, count);
-               failed = o_stream_send_istream(output, input) < 0;
-               if (failed) {
-                       errno = output->stream_errno;
-                       subsfile_set_syscall_error(storage,
-                                                  "o_stream_send_istream()",
-                                                  path);
-               }
-       }
-
-       if (!failed) {
-               if (ftruncate(fd, output->offset) < 0) {
-                       subsfile_set_syscall_error(storage, "ftruncate()",
-                                                  path);
-                       failed = TRUE;
-               }
-       }
-
-       o_stream_unref(output);
-       return !failed;
-}
-
 int subsfile_set_subscribed(struct mail_storage *storage,
                            const char *name, int set)
 {
        const char *path, *line;
        struct istream *input;
-       uoff_t offset;
-       int fd, failed;
+       struct ostream *output;
+       int fd_in, fd_out, found, failed = FALSE;
 
        if (strcasecmp(name, "INBOX") == 0)
                name = "INBOX";
 
-       fd = subscription_open(storage, TRUE, &path);
-       if (fd == -1)
+       path = t_strconcat(storage->control_dir != NULL ?
+                          storage->control_dir : storage->dir,
+                          "/" SUBSCRIPTION_FILE_NAME, NULL);
+       /* FIXME: set lock notification callback */
+       fd_out = file_dotlock_open(path, NULL, SUBSCRIPTION_FILE_LOCK_TIMEOUT,
+                                  SUBSCRIPTION_FILE_STALE_TIMEOUT, NULL, NULL);
+       if (fd_out == -1) {
+               if (errno == EAGAIN) {
+                       mail_storage_set_error(storage,
+                               "Timeout waiting for subscription file lock");
+               } else {
+                       subsfile_set_syscall_error(storage,
+                                                  "file_dotlock_open()", path);
+               }
+               return FALSE;
+       }
+
+       fd_in = open(path, O_RDONLY);
+       if (fd_in == -1 && errno != ENOENT) {
+               subsfile_set_syscall_error(storage, "open()", path);
+               file_dotlock_delete(path, fd_out);
                return FALSE;
+       }
 
-       input = i_stream_create_file(fd, default_pool,
-                                    MAX_MAILBOX_LENGTH, FALSE);
-       do {
-               offset = input->v_offset;
-                line = next_line(storage, path, input, &failed);
-       } while (line != NULL && strcmp(line, name) != 0);
-
-       if (!failed) {
-               if (set && line == NULL) {
-                       /* add subscription. we're at EOF so just write it */
-                       write_full(fd, t_strconcat(name, "\n", NULL),
-                                  strlen(name)+1);
-               } else if (!set && line != NULL) {
-                       /* remove subcription. */
-                       uoff_t size = input->v_offset - offset;
-                       i_stream_seek(input, offset);
-                       if (!stream_cut(storage, path, input, size))
+       input = i_stream_create_file(fd_in, default_pool,
+                                    MAX_MAILBOX_LENGTH, TRUE);
+       output = o_stream_create_file(fd_out, default_pool,
+                                     MAX_MAILBOX_LENGTH, FALSE);
+       found = FALSE;
+       while ((line = next_line(storage, path, input, &failed)) != NULL) {
+               if (strcmp(line, name) == 0) {
+                       found = TRUE;
+                       if (set)
+                               break;
+               } else {
+                       if (o_stream_send_str(output, line) < 0 ||
+                           o_stream_send(output, "\n", 1) < 0) {
+                               subsfile_set_syscall_error(storage, "write()",
+                                                          path);
                                failed = TRUE;
+                               break;
+                       }
+               }
+       }
+
+       if (!failed && set && !found) {
+               /* append subscription */
+               line = t_strconcat(name, "\n", NULL);
+               if (o_stream_send_str(output, line) < 0) {
+                       subsfile_set_syscall_error(storage, "write()", path);
+                       failed = TRUE;
                }
        }
 
        i_stream_unref(input);
+       o_stream_unref(output);
 
-       if (close(fd) < 0) {
-               subsfile_set_syscall_error(storage, "close()", path);
-               failed = TRUE;
+       if (failed || (set && found) || (!set && !found)) {
+               if (file_dotlock_delete(path, fd_out) < 0) {
+                       subsfile_set_syscall_error(storage,
+                               "file_dotlock_delete()", path);
+                       failed = TRUE;
+               }
+       } else {
+               if (file_dotlock_replace(path, fd_out, TRUE) < 0) {
+                       subsfile_set_syscall_error(storage,
+                               "file_dotlock_replace()", path);
+                       failed = TRUE;
+               }
        }
        return !failed;
 }
@@ -183,9 +158,14 @@ subsfile_list_init(struct mail_storage *storage)
        const char *path;
        int fd;
 
-       fd = subscription_open(storage, FALSE, &path);
-       if (fd == -1 && errno != ENOENT)
+       path = t_strconcat(storage->control_dir != NULL ?
+                          storage->control_dir : storage->dir,
+                          "/" SUBSCRIPTION_FILE_NAME, NULL);
+       fd = open(path, O_RDONLY);
+       if (fd == -1 && errno != ENOENT) {
+               subsfile_set_syscall_error(storage, "open()", path);
                return NULL;
+       }
 
        pool = pool_alloconly_create("subsfile_list", MAX_MAILBOX_LENGTH+1024);