From d424adcf6301d996942eef1332f3a61e16b709c2 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Thu, 16 Nov 2000 13:27:21 +0000 Subject: Check the uid string is all digits before trying to write a 'standard' 2000-11-16 Not Zed * providers/local/camel-local-summary.c (local_summary_encode_x_evolution): Check the uid string is all digits before trying to write a 'standard' x-ev header. * providers/local/camel-maildir-summary.c (camel_maildir_summary_info_to_name): Convert an info into a maildir name:info filename. (camel_maildir_summary_name_to_info): Convert a name:info filename into an info, and tell us if it didn't match it. (message_info_new): When creating a new filename, gets its info from the flags field. Likewise if creating from an existing file, extract the flags. (maildir_summary_sync): Remove a small memleak. Also, if our flags and that requires a filename change, perform that here. (message_info_new): Get the received date from the filename. Also, dont overwirte the uid if we have one. (maildir_summary_check): Sort the summary in received order before completion. (maildir_summary_next_uid_string): Test the name for collusions before we give it out. Retry, and if that fails, well, I guess we collide :( * providers/local/camel-mbox-folder.c (mbox_lock): Implement mbox locking. (mbox_unlock): And unlocking. (mbox_append_message): Lock the folder for write before doing anything. (mbox_get_message): Lock the folder for read before doing anything. * providers/local/camel-local-folder.c (camel_local_folder_lock): Implement something here. We handle the recursive ability but pass the locking to the folder itself. (camel_local_folder_unlock): Likewise for unlocking. (local_lock): Default - do nothing, return success. (local_unlock): Same. (local_sync): Changed slightly for locking api changes, and also, only lock around the sync process itself. * camel-lock.c: New file - utility functions for locking using different strategies and/or for locking folders safely. * Makefile.am (libcamel_la_SOURCES): Adde camel-lock.[ch] svn path=/trunk/; revision=6592 --- camel/providers/local/camel-mbox-folder.c | 86 +++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 17 deletions(-) (limited to 'camel/providers/local/camel-mbox-folder.c') diff --git a/camel/providers/local/camel-mbox-folder.c b/camel/providers/local/camel-mbox-folder.c index d12b6431de..d65ad54c01 100644 --- a/camel/providers/local/camel-mbox-folder.c +++ b/camel/providers/local/camel-mbox-folder.c @@ -51,6 +51,8 @@ static CamelLocalFolderClass *parent_class = NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CMBOXS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so)) +static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); +static void mbox_unlock(CamelLocalFolder *lf); static void mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex); static CamelMimeMessage *mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex); @@ -73,19 +75,25 @@ camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class) camel_folder_class->get_message = mbox_get_message; lclass->create_summary = mbox_create_summary; + lclass->lock = mbox_lock; + lclass->unlock = mbox_unlock; } static void mbox_init(gpointer object, gpointer klass) { - /*CamelFolder *folder = object; - CamelMboxFolder *mbox_folder = object;*/ + /*CamelFolder *folder = object;*/ + CamelMboxFolder *mbox_folder = object; + + mbox_folder->lockfd = -1; } static void mbox_finalise(CamelObject * object) { - /*CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(object);*/ + CamelMboxFolder *mbox_folder = (CamelMboxFolder *)object; + + g_assert(mbox_folder->lockfd == -1); } CamelType camel_mbox_folder_get_type(void) @@ -124,6 +132,32 @@ static CamelLocalSummary *mbox_create_summary(const char *path, const char *fold return (CamelLocalSummary *)camel_mbox_summary_new(path, folder, index); } +static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex) +{ + CamelMboxFolder *mf = (CamelMboxFolder *)lf; + + /* make sure we have matching unlocks for locks, camel-local-folder class should enforce this */ + g_assert(mf->lockfd == -1); + + mf->lockfd = open(lf->folder_path, O_RDWR, 0); + if (mf->lockfd == -1) { + camel_exception_setv(ex, 1, "Cannot create folder lock on %s: %s", lf->folder_path, strerror(errno)); + return -1; + } + + return camel_lock_folder(lf->folder_path, mf->lockfd, type, ex); +} + +static void mbox_unlock(CamelLocalFolder *lf) +{ + CamelMboxFolder *mf = (CamelMboxFolder *)lf; + + g_assert(mf->lockfd != -1); + camel_unlock_folder(lf->folder_path, mf->lockfd); + close(mf->lockfd); + mf->lockfd = -1; +} + static void mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex) { @@ -136,27 +170,28 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel int fd; struct stat st; - /* FIXME: Locking */ + /* If we can't lock, dont do anything */ + if (camel_local_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1) + return; d(printf("Appending message\n")); /* first, check the summary is correct (updates folder_size too) */ camel_local_summary_check(lf->summary, lf->changes, ex); if (camel_exception_is_set(ex)) - return; + goto fail; /* add it to the summary/assign the uid, etc */ mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex); - if (camel_exception_is_set(ex)) { - return; - } + if (camel_exception_is_set(ex)) + goto fail; d(printf("Appending message: uid is %s\n", mi->uid)); output_stream = camel_stream_fs_new_with_name(lf->folder_path, O_WRONLY|O_APPEND, 0600); if (output_stream == NULL) { camel_exception_setv(ex, 1, _("Cannot open mailbox: %s: %s\n"), lf->folder_path, strerror(errno)); - return; + goto fail; } /* we must write this to the non-filtered stream ... prepend a \n if not at the start of the file */ @@ -174,12 +209,8 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel if (camel_stream_close(filter_stream) == -1) goto fail_write; - /* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */ - /* the stat really shouldn't fail, we just wrote to it */ - if (stat(lf->folder_path, &st) == 0) { - mbs->folder_size = st.st_size; - ((CamelFolderSummary *)mbs)->time = st.st_mtime; - } + /* unlock as soon as we can */ + camel_local_folder_unlock(lf); /* filter stream ref's the output stream itself, so we need to unref it too */ camel_object_unref((CamelObject *)filter_from); @@ -187,6 +218,13 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel camel_object_unref((CamelObject *)output_stream); g_free(fromline); + /* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */ + /* the stat really shouldn't fail, we just wrote to it */ + if (stat(lf->folder_path, &st) == 0) { + mbs->folder_size = st.st_size; + ((CamelFolderSummary *)mbs)->time = st.st_mtime; + } + if (camel_folder_change_info_changed(lf->changes)) { camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes); camel_folder_change_info_clear(lf->changes); @@ -223,6 +261,10 @@ fail_write: ((CamelFolderSummary *)mbs)->time = st.st_mtime; } +fail: + /* make sure we unlock the folder - before we start triggering events into appland */ + camel_local_folder_unlock(lf); + /* cascade the changes through, anyway, if there are any outstanding */ if (camel_folder_change_info_changed(lf->changes)) { camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes); @@ -242,7 +284,9 @@ mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex) d(printf("Getting message %s\n", uid)); - /* FIXME: mbox locking */ + /* lock the folder first, burn if we can't */ + if (camel_local_folder_lock(lf, CAMEL_LOCK_READ, ex) == -1) + return NULL; retry: /* get the message summary info */ @@ -251,6 +295,7 @@ retry: if (info == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"), uid, _("No such message")); + camel_local_folder_unlock(lf); return NULL; } @@ -260,13 +305,15 @@ retry: /* we use an fd instead of a normal stream here - the reason is subtle, camel_mime_part will cache the whole message in memory if the stream is non-seekable (which it is when built from a parser - with no stream). This means we dont have to lock the mbox for the life of the message. */ + with no stream). This means we dont have to lock the mbox for the life of the message, but only + while it is being created. */ fd = open(lf->folder_path, O_RDONLY); if (fd == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, strerror(errno)); + camel_local_folder_unlock(lf); return NULL; } @@ -296,6 +343,7 @@ retry: _("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path, _("The folder appears to be irrecoverably corrupted.")); + camel_local_folder_unlock(lf); return NULL; } @@ -307,9 +355,13 @@ retry: _("Message construction failed: Corrupt mailbox?")); camel_object_unref((CamelObject *)parser); camel_object_unref((CamelObject *)message); + camel_local_folder_unlock(lf); return NULL; } + /* and unlock now we're finished with it */ + camel_local_folder_unlock(lf); + camel_object_unref((CamelObject *)parser); /* use the opportunity to notify of changes (particularly if we had a rebuild) */ -- cgit