1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2001-2003 Ximian Inc.
5 * Authors: Michael Zucchi <notzed@ximian.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
33 #include <sys/types.h>
36 #include <glib/gstdio.h>
38 #include <libedataserver/e-msgport.h>
40 #include "camel-block-file.h"
41 #include "camel-file-utils.h"
42 #include "camel-private.h"
44 #define d(x) /*(printf("%s(%d):%s: ", __FILE__, __LINE__, __PRETTY_FUNCTION__),(x))*/
46 /* Locks must be obtained in the order defined */
48 struct _CamelBlockFilePrivate {
49 /* We use the private structure to form our lru list from */
50 struct _CamelBlockFilePrivate *next;
51 struct _CamelBlockFilePrivate *prev;
53 struct _CamelBlockFile *base;
55 pthread_mutex_t root_lock; /* for modifying the root block */
56 pthread_mutex_t cache_lock; /* for refcounting, flag manip, cache manip */
57 pthread_mutex_t io_lock; /* for all io ops */
59 unsigned int deleted:1;
63 #define CAMEL_BLOCK_FILE_LOCK(kf, lock) (pthread_mutex_lock(&(kf)->priv->lock))
64 #define CAMEL_BLOCK_FILE_TRYLOCK(kf, lock) (pthread_mutex_trylock(&(kf)->priv->lock))
65 #define CAMEL_BLOCK_FILE_UNLOCK(kf, lock) (pthread_mutex_unlock(&(kf)->priv->lock))
67 #define LOCK(x) pthread_mutex_lock(&x)
68 #define UNLOCK(x) pthread_mutex_unlock(&x)
70 static pthread_mutex_t block_file_lock = PTHREAD_MUTEX_INITIALIZER;
72 /* lru cache of block files */
73 static EDList block_file_list = E_DLIST_INITIALISER(block_file_list);
74 /* list to store block files that are actually intialised */
75 static EDList block_file_active_list = E_DLIST_INITIALISER(block_file_active_list);
76 static int block_file_count = 0;
77 static int block_file_threshhold = 10;
79 #define CBF_CLASS(o) ((CamelBlockFileClass *)(((CamelObject *)o)->klass))
81 static int sync_nolock(CamelBlockFile *bs);
82 static int sync_block_nolock(CamelBlockFile *bs, CamelBlock *bl);
85 block_file_validate_root(CamelBlockFile *bs)
93 retval = fstat (bs->fd, &st);
95 d(printf("Validate root: '%s'\n", bs->path));
96 d(printf("version: %.8s (%.8s)\n", bs->root->version, bs->version));
97 d(printf("block size: %d (%d)%s\n", br->block_size, bs->block_size,
98 br->block_size != bs->block_size ? " BAD":" OK"));
99 d(printf("free: %ld (%d add size < %ld)%s\n", (long)br->free, br->free / bs->block_size * bs->block_size, (long)st.st_size,
100 (br->free > st.st_size) || (br->free % bs->block_size) != 0 ? " BAD":" OK"));
101 d(printf("last: %ld (%d and size: %ld)%s\n", (long)br->last, br->last / bs->block_size * bs->block_size, (long)st.st_size,
102 (br->last != st.st_size) || ((br->last % bs->block_size) != 0) ? " BAD": " OK"));
103 d(printf("flags: %s\n", (br->flags & CAMEL_BLOCK_FILE_SYNC)?"SYNC":"unSYNC"));
106 || memcmp(bs->root->version, bs->version, 8) != 0
107 || br->block_size != bs->block_size
108 || (br->free % bs->block_size) != 0
109 || (br->last % bs->block_size) != 0
111 || st.st_size != br->last
112 || br->free > st.st_size
113 || (br->flags & CAMEL_BLOCK_FILE_SYNC) == 0) {
115 if (retval != -1 && st.st_size > 0) {
116 g_warning("Invalid root: '%s'", bs->path);
117 g_warning("version: %.8s (%.8s)", bs->root->version, bs->version);
118 g_warning("block size: %d (%d)%s", br->block_size, bs->block_size,
119 br->block_size != bs->block_size ? " BAD":" OK");
120 g_warning("free: %ld (%d add size < %ld)%s", (long)br->free, br->free / bs->block_size * bs->block_size, (long)st.st_size,
121 (br->free > st.st_size) || (br->free % bs->block_size) != 0 ? " BAD":" OK");
122 g_warning("last: %ld (%d and size: %ld)%s", (long)br->last, br->last / bs->block_size * bs->block_size, (long)st.st_size,
123 (br->last != st.st_size) || ((br->last % bs->block_size) != 0) ? " BAD": " OK");
124 g_warning("flags: %s", (br->flags & CAMEL_BLOCK_FILE_SYNC)?"SYNC":"unSYNC");
134 block_file_init_root(CamelBlockFile *bs)
136 CamelBlockRoot *br = bs->root;
138 memset(br, 0, bs->block_size);
139 memcpy(br->version, bs->version, 8);
140 br->last = bs->block_size;
141 br->flags = CAMEL_BLOCK_FILE_SYNC;
143 br->block_size = bs->block_size;
149 camel_block_file_class_init(CamelBlockFileClass *klass)
151 klass->validate_root = block_file_validate_root;
152 klass->init_root = block_file_init_root;
156 block_hash_func(const void *v)
158 return ((camel_block_t) GPOINTER_TO_UINT(v)) >> CAMEL_BLOCK_SIZE_BITS;
162 camel_block_file_init(CamelBlockFile *bs)
164 struct _CamelBlockFilePrivate *p;
167 bs->block_size = CAMEL_BLOCK_SIZE;
168 e_dlist_init(&bs->block_cache);
169 bs->blocks = g_hash_table_new((GHashFunc)block_hash_func, NULL);
170 /* this cache size and the text index size have been tuned for about the best
171 with moderate memory usage. Doubling the memory usage barely affects performance. */
172 bs->block_cache_limit = 256;
174 p = bs->priv = g_malloc0(sizeof(*bs->priv));
177 pthread_mutex_init(&p->root_lock, NULL);
178 pthread_mutex_init(&p->cache_lock, NULL);
179 pthread_mutex_init(&p->io_lock, NULL);
181 /* link into lru list */
182 LOCK(block_file_lock);
183 e_dlist_addhead(&block_file_list, (EDListNode *)p);
187 printf("dumping block list\n");
188 printf(" head = %p p = %p\n", block_file_list.head, p);
189 p = block_file_list.head;
191 printf(" '%s'\n", p->base->path);
197 UNLOCK(block_file_lock);
201 camel_block_file_finalise(CamelBlockFile *bs)
204 struct _CamelBlockFilePrivate *p;
209 camel_block_file_sync(bs);
211 /* remove from lru list */
212 LOCK(block_file_lock);
215 e_dlist_remove((EDListNode *)p);
216 UNLOCK(block_file_lock);
218 bl = (CamelBlock *)bs->block_cache.head;
221 if (bl->refcount != 0)
222 g_warning("Block '%u' still referenced", bl->id);
228 g_hash_table_destroy (bs->blocks);
231 camel_block_file_unref_block(bs, bs->root_block);
236 pthread_mutex_destroy(&p->io_lock);
237 pthread_mutex_destroy(&p->cache_lock);
238 pthread_mutex_destroy(&p->root_lock);
244 camel_block_file_get_type(void)
246 static CamelType type = CAMEL_INVALID_TYPE;
248 if (type == CAMEL_INVALID_TYPE) {
249 type = camel_type_register(camel_object_get_type(), "CamelBlockFile",
250 sizeof (CamelBlockFile),
251 sizeof (CamelBlockFileClass),
252 (CamelObjectClassInitFunc) camel_block_file_class_init,
254 (CamelObjectInitFunc) camel_block_file_init,
255 (CamelObjectFinalizeFunc) camel_block_file_finalise);
261 /* 'use' a block file for io */
263 block_file_use(CamelBlockFile *bs)
265 struct _CamelBlockFilePrivate *nw, *nn, *p = bs->priv;
270 remove file from active list
275 add it back to end of active list
278 CAMEL_BLOCK_FILE_LOCK(bs, io_lock);
282 else if (p->deleted) {
283 CAMEL_BLOCK_FILE_UNLOCK(bs, io_lock);
287 d(printf("Turning block file online: %s\n", bs->path));
289 if ((bs->fd = g_open(bs->path, bs->flags|O_BINARY, 0600)) == -1) {
291 CAMEL_BLOCK_FILE_UNLOCK(bs, io_lock);
296 LOCK(block_file_lock);
297 e_dlist_remove((EDListNode *)p);
298 e_dlist_addtail(&block_file_active_list, (EDListNode *)p);
302 nw = (struct _CamelBlockFilePrivate *)block_file_list.head;
304 while (block_file_count > block_file_threshhold && nn) {
305 /* We never hit the current blockfile here, as its removed from the list first */
308 /* Need to trylock, as any of these lock levels might be trying
309 to lock the block_file_lock, so we need to check and abort if so */
310 if (CAMEL_BLOCK_FILE_TRYLOCK(bf, root_lock) == 0) {
311 if (CAMEL_BLOCK_FILE_TRYLOCK(bf, cache_lock) == 0) {
312 if (CAMEL_BLOCK_FILE_TRYLOCK(bf, io_lock) == 0) {
313 d(printf("[%d] Turning block file offline: %s\n", block_file_count-1, bf->path));
318 CAMEL_BLOCK_FILE_UNLOCK(bf, io_lock);
320 CAMEL_BLOCK_FILE_UNLOCK(bf, cache_lock);
322 CAMEL_BLOCK_FILE_UNLOCK(bf, root_lock);
329 UNLOCK(block_file_lock);
335 block_file_unuse(CamelBlockFile *bs)
337 LOCK(block_file_lock);
338 e_dlist_remove((EDListNode *)bs->priv);
339 e_dlist_addtail(&block_file_list, (EDListNode *)bs->priv);
340 UNLOCK(block_file_lock);
342 CAMEL_BLOCK_FILE_UNLOCK(bs, io_lock);
346 o = camel_cache_get(c, key);
347 camel_cache_unref(c, key);
348 camel_cache_add(c, key, o);
349 camel_cache_remove(c, key);
353 * camel_block_file_new:
358 * Allocate a new block file, stored at @path. @version contains an 8 character
359 * version string which must match the head of the file, or the file will be
362 * @block_size is currently ignored and is set to CAMEL_BLOCK_SIZE.
364 * Return value: The new block file, or NULL if it could not be created.
366 CamelBlockFile *camel_block_file_new(const char *path, int flags, const char version[8], size_t block_size)
370 bs = (CamelBlockFile *)camel_object_new(camel_block_file_get_type());
371 memcpy(bs->version, version, 8);
372 bs->path = g_strdup(path);
375 bs->root_block = camel_block_file_get_block(bs, 0);
376 if (bs->root_block == NULL) {
377 camel_object_unref((CamelObject *)bs);
380 camel_block_file_detach_block(bs, bs->root_block);
381 bs->root = (CamelBlockRoot *)&bs->root_block->data;
383 /* we only need these flags on first open */
384 bs->flags &= ~(O_CREAT|O_EXCL|O_TRUNC);
386 /* Do we need to init the root block? */
387 if (CBF_CLASS(bs)->validate_root(bs) == -1) {
388 d(printf("Initialise root block: %.8s\n", version));
390 CBF_CLASS(bs)->init_root(bs);
391 camel_block_file_touch_block(bs, bs->root_block);
392 if (block_file_use(bs) == -1) {
393 camel_object_unref((CamelObject *)bs);
396 if (sync_block_nolock(bs, bs->root_block) == -1
397 || ftruncate(bs->fd, bs->root->last) == -1) {
398 block_file_unuse(bs);
399 camel_object_unref((CamelObject *)bs);
402 block_file_unuse(bs);
409 camel_block_file_rename(CamelBlockFile *bs, const char *path)
415 CAMEL_BLOCK_FILE_LOCK(bs, io_lock);
417 ret = g_rename(bs->path, path);
419 /* Maybe the rename actually worked */
421 if (g_stat(path, &st) == 0
422 && g_stat(bs->path, &st) == -1
430 bs->path = g_strdup(path);
433 CAMEL_BLOCK_FILE_UNLOCK(bs, io_lock);
439 camel_block_file_delete(CamelBlockFile *bs)
442 struct _CamelBlockFilePrivate *p = bs->priv;
444 CAMEL_BLOCK_FILE_LOCK(bs, io_lock);
447 LOCK(block_file_lock);
449 UNLOCK(block_file_lock);
455 ret = g_unlink(bs->path);
457 CAMEL_BLOCK_FILE_UNLOCK(bs, io_lock);
464 * camel_block_file_new_block:
467 * Allocate a new block, return a pointer to it. Old blocks
468 * may be flushed to disk during this call.
470 * Return value: The block, or NULL if an error occured.
472 CamelBlock *camel_block_file_new_block(CamelBlockFile *bs)
476 CAMEL_BLOCK_FILE_LOCK(bs, root_lock);
478 if (bs->root->free) {
479 bl = camel_block_file_get_block(bs, bs->root->free);
482 bs->root->free = ((camel_block_t *)bl->data)[0];
484 bl = camel_block_file_get_block(bs, bs->root->last);
487 bs->root->last += CAMEL_BLOCK_SIZE;
490 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
492 bl->flags |= CAMEL_BLOCK_DIRTY;
493 memset(bl->data, 0, CAMEL_BLOCK_SIZE);
495 CAMEL_BLOCK_FILE_UNLOCK(bs, root_lock);
501 * camel_block_file_free_block:
507 int camel_block_file_free_block(CamelBlockFile *bs, camel_block_t id)
511 bl = camel_block_file_get_block(bs, id);
515 CAMEL_BLOCK_FILE_LOCK(bs, root_lock);
517 ((camel_block_t *)bl->data)[0] = bs->root->free;
518 bs->root->free = bl->id;
519 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
520 bl->flags |= CAMEL_BLOCK_DIRTY;
521 camel_block_file_unref_block(bs, bl);
523 CAMEL_BLOCK_FILE_UNLOCK(bs, root_lock);
529 * camel_block_file_get_block:
533 * Retreive a block @id.
535 * Return value: The block, or NULL if blockid is invalid or a file error
538 CamelBlock *camel_block_file_get_block(CamelBlockFile *bs, camel_block_t id)
540 CamelBlock *bl, *flush, *prev;
542 /* Sanity check: Dont allow reading of root block (except before its been read)
543 or blocks with invalid block id's */
544 if ((bs->root == NULL && id != 0)
545 || (bs->root != NULL && (id > bs->root->last || id == 0))
546 || (id % bs->block_size) != 0) {
551 CAMEL_BLOCK_FILE_LOCK(bs, cache_lock);
553 bl = g_hash_table_lookup(bs->blocks, GUINT_TO_POINTER(id));
555 d(printf("Get block %08x: %s\n", id, bl?"cached":"must read"));
559 if (block_file_use(bs) == -1) {
560 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
564 bl = g_malloc0(sizeof(*bl));
566 if (lseek(bs->fd, id, SEEK_SET) == -1 ||
567 camel_read (bs->fd, bl->data, CAMEL_BLOCK_SIZE) == -1) {
568 block_file_unuse(bs);
569 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
574 bs->block_cache_count++;
575 g_hash_table_insert(bs->blocks, GUINT_TO_POINTER(bl->id), bl);
577 /* flush old blocks */
578 flush = (CamelBlock *)bs->block_cache.tailpred;
580 while (bs->block_cache_count > bs->block_cache_limit && prev) {
581 if (flush->refcount == 0) {
582 if (sync_block_nolock(bs, flush) != -1) {
583 g_hash_table_remove(bs->blocks, GUINT_TO_POINTER(flush->id));
584 e_dlist_remove((EDListNode *)flush);
586 bs->block_cache_count--;
593 block_file_unuse(bs);
595 e_dlist_remove((EDListNode *)bl);
598 e_dlist_addhead(&bs->block_cache, (EDListNode *)bl);
601 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
603 d(printf("Got block %08x\n", id));
609 * camel_block_file_detach_block:
613 * Detatch a block from the block file's cache. The block should
614 * be unref'd or attached when finished with. The block file will
615 * perform no writes of this block or flushing of it if the cache
618 void camel_block_file_detach_block(CamelBlockFile *bs, CamelBlock *bl)
620 CAMEL_BLOCK_FILE_LOCK(bs, cache_lock);
622 g_hash_table_remove(bs->blocks, GUINT_TO_POINTER(bl->id));
623 e_dlist_remove((EDListNode *)bl);
624 bl->flags |= CAMEL_BLOCK_DETACHED;
626 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
630 * camel_block_file_attach_block:
634 * Reattach a block that has been detached.
636 void camel_block_file_attach_block(CamelBlockFile *bs, CamelBlock *bl)
638 CAMEL_BLOCK_FILE_LOCK(bs, cache_lock);
640 g_hash_table_insert(bs->blocks, GUINT_TO_POINTER(bl->id), bl);
641 e_dlist_addtail(&bs->block_cache, (EDListNode *)bl);
642 bl->flags &= ~CAMEL_BLOCK_DETACHED;
644 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
648 * camel_block_file_touch_block:
652 * Mark a block as dirty. The block will be written to disk if
653 * it ever expires from the cache.
655 void camel_block_file_touch_block(CamelBlockFile *bs, CamelBlock *bl)
657 CAMEL_BLOCK_FILE_LOCK(bs, root_lock);
658 CAMEL_BLOCK_FILE_LOCK(bs, cache_lock);
660 bl->flags |= CAMEL_BLOCK_DIRTY;
662 if ((bs->root->flags & CAMEL_BLOCK_FILE_SYNC) && bl != bs->root_block) {
663 d(printf("turning off sync flag\n"));
664 bs->root->flags &= ~CAMEL_BLOCK_FILE_SYNC;
665 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
666 camel_block_file_sync_block(bs, bs->root_block);
669 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
670 CAMEL_BLOCK_FILE_UNLOCK(bs, root_lock);
674 * camel_block_file_unref_block:
678 * Mark a block as unused. If a block is used it will not be
679 * written to disk, or flushed from memory.
681 * If a block is detatched and this is the last reference, the
682 * block will be freed.
684 void camel_block_file_unref_block(CamelBlockFile *bs, CamelBlock *bl)
686 CAMEL_BLOCK_FILE_LOCK(bs, cache_lock);
688 if (bl->refcount == 1 && (bl->flags & CAMEL_BLOCK_DETACHED))
693 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
697 sync_block_nolock(CamelBlockFile *bs, CamelBlock *bl)
699 d(printf("Sync block %08x: %s\n", bl->id, (bl->flags & CAMEL_BLOCK_DIRTY)?"dirty":"clean"));
701 if (bl->flags & CAMEL_BLOCK_DIRTY) {
702 if (lseek(bs->fd, bl->id, SEEK_SET) == -1
703 || write(bs->fd, bl->data, CAMEL_BLOCK_SIZE) != CAMEL_BLOCK_SIZE) {
706 bl->flags &= ~CAMEL_BLOCK_DIRTY;
713 sync_nolock(CamelBlockFile *bs)
718 bl = (CamelBlock *)bs->block_cache.head;
721 if (bl->flags & CAMEL_BLOCK_DIRTY) {
723 if (sync_block_nolock(bs, bl) == -1)
731 && (bs->root_block->flags & CAMEL_BLOCK_DIRTY) == 0
732 && (bs->root->flags & CAMEL_BLOCK_FILE_SYNC) != 0)
735 d(printf("turning on sync flag\n"));
737 bs->root->flags |= CAMEL_BLOCK_FILE_SYNC;
738 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
740 return sync_block_nolock(bs, bs->root_block);
744 * camel_block_file_sync_block:
748 * Flush a block to disk immediately. The block will only
749 * be flushed to disk if it is marked as dirty (touched).
751 * Return value: -1 on io error.
753 int camel_block_file_sync_block(CamelBlockFile *bs, CamelBlock *bl)
758 if (block_file_use(bs) == -1)
761 ret = sync_block_nolock(bs, bl);
763 block_file_unuse(bs);
769 * camel_block_file_sync:
772 * Sync all dirty blocks to disk, including the root block.
774 * Return value: -1 on io error.
776 int camel_block_file_sync(CamelBlockFile *bs)
780 CAMEL_BLOCK_FILE_LOCK(bs, root_lock);
781 CAMEL_BLOCK_FILE_LOCK(bs, cache_lock);
784 if (block_file_use(bs) == -1)
787 ret = sync_nolock(bs);
788 block_file_unuse(bs);
791 CAMEL_BLOCK_FILE_UNLOCK(bs, cache_lock);
792 CAMEL_BLOCK_FILE_UNLOCK(bs, root_lock);
797 /* ********************************************************************** */
799 struct _CamelKeyFilePrivate {
800 struct _CamelKeyFilePrivate *next;
801 struct _CamelKeyFilePrivate *prev;
803 struct _CamelKeyFile *base;
804 pthread_mutex_t lock;
805 unsigned int deleted:1;
808 #define CAMEL_KEY_FILE_LOCK(kf, lock) (pthread_mutex_lock(&(kf)->priv->lock))
809 #define CAMEL_KEY_FILE_TRYLOCK(kf, lock) (pthread_mutex_trylock(&(kf)->priv->lock))
810 #define CAMEL_KEY_FILE_UNLOCK(kf, lock) (pthread_mutex_unlock(&(kf)->priv->lock))
812 static pthread_mutex_t key_file_lock = PTHREAD_MUTEX_INITIALIZER;
814 /* lru cache of block files */
815 static EDList key_file_list = E_DLIST_INITIALISER(key_file_list);
816 static EDList key_file_active_list = E_DLIST_INITIALISER(key_file_active_list);
817 static int key_file_count = 0;
818 static const int key_file_threshhold = 10;
821 camel_key_file_class_init(CamelKeyFileClass *klass)
826 camel_key_file_init(CamelKeyFile *bs)
828 struct _CamelKeyFilePrivate *p;
830 p = bs->priv = g_malloc0(sizeof(*bs->priv));
833 pthread_mutex_init(&p->lock, NULL);
836 e_dlist_addhead(&key_file_list, (EDListNode *)p);
837 UNLOCK(key_file_lock);
841 camel_key_file_finalise(CamelKeyFile *bs)
843 struct _CamelKeyFilePrivate *p = bs->priv;
846 e_dlist_remove((EDListNode *)p);
853 UNLOCK(key_file_lock);
857 pthread_mutex_destroy(&p->lock);
863 camel_key_file_get_type(void)
865 static CamelType type = CAMEL_INVALID_TYPE;
867 if (type == CAMEL_INVALID_TYPE) {
868 type = camel_type_register(camel_object_get_type(), "CamelKeyFile",
869 sizeof (CamelKeyFile),
870 sizeof (CamelKeyFileClass),
871 (CamelObjectClassInitFunc) camel_key_file_class_init,
873 (CamelObjectInitFunc) camel_key_file_init,
874 (CamelObjectFinalizeFunc) camel_key_file_finalise);
880 /* 'use' a key file for io */
882 key_file_use(CamelKeyFile *bs)
884 struct _CamelKeyFilePrivate *nw, *nn, *p = bs->priv;
890 remove file from active list
895 add it back to end of active list
898 /* TODO: Check header on reset? */
900 CAMEL_KEY_FILE_LOCK(bs, lock);
904 else if (p->deleted) {
905 CAMEL_KEY_FILE_UNLOCK(bs, lock);
909 d(printf("Turning key file online: '%s'\n", bs->path));
911 if ((bs->flags & O_ACCMODE) == O_RDONLY)
916 if ((fd = g_open(bs->path, bs->flags|O_BINARY, 0600)) == -1
917 || (bs->fp = fdopen(fd, flag)) == NULL) {
921 CAMEL_KEY_FILE_UNLOCK(bs, lock);
927 e_dlist_remove((EDListNode *)p);
928 e_dlist_addtail(&key_file_active_list, (EDListNode *)p);
932 nw = (struct _CamelKeyFilePrivate *)key_file_list.head;
934 while (key_file_count > key_file_threshhold && nn) {
935 /* We never hit the current keyfile here, as its removed from the list first */
937 if (bf->fp != NULL) {
938 /* Need to trylock, as any of these lock levels might be trying
939 to lock the key_file_lock, so we need to check and abort if so */
940 if (CAMEL_BLOCK_FILE_TRYLOCK(bf, lock) == 0) {
941 d(printf("Turning key file offline: %s\n", bf->path));
945 CAMEL_BLOCK_FILE_UNLOCK(bf, lock);
952 UNLOCK(key_file_lock);
958 key_file_unuse(CamelKeyFile *bs)
961 e_dlist_remove((EDListNode *)bs->priv);
962 e_dlist_addtail(&key_file_list, (EDListNode *)bs->priv);
963 UNLOCK(key_file_lock);
965 CAMEL_KEY_FILE_UNLOCK(bs, lock);
969 * camel_key_file_new:
972 * @version: Version string (header) of file. Currently
973 * written but not checked.
975 * Create a new key file. A linked list of record blocks.
977 * Return value: A new key file, or NULL if the file could not
978 * be opened/created/initialised.
981 camel_key_file_new(const char *path, int flags, const char version[8])
987 d(printf("New key file '%s'\n", path));
989 kf = (CamelKeyFile *)camel_object_new(camel_key_file_get_type());
990 kf->path = g_strdup(path);
995 if (key_file_use(kf) == -1) {
996 camel_object_unref((CamelObject *)kf);
999 fseek(kf->fp, 0, SEEK_END);
1000 last = ftell(kf->fp);
1002 fwrite(version, 8, 1, kf->fp);
1007 err = ferror(kf->fp);
1010 /* we only need these flags on first open */
1011 kf->flags &= ~(O_CREAT|O_EXCL|O_TRUNC);
1014 camel_object_unref((CamelObject *)kf);
1023 camel_key_file_rename(CamelKeyFile *kf, const char *path)
1029 CAMEL_KEY_FILE_LOCK(kf, lock);
1031 ret = g_rename(kf->path, path);
1033 /* Maybe the rename actually worked */
1035 if (g_stat(path, &st) == 0
1036 && g_stat(kf->path, &st) == -1
1044 kf->path = g_strdup(path);
1047 CAMEL_KEY_FILE_UNLOCK(kf, lock);
1053 camel_key_file_delete(CamelKeyFile *kf)
1056 struct _CamelKeyFilePrivate *p = kf->priv;
1058 CAMEL_KEY_FILE_LOCK(kf, lock);
1061 LOCK(key_file_lock);
1063 UNLOCK(key_file_lock);
1069 ret = g_unlink(kf->path);
1071 CAMEL_KEY_FILE_UNLOCK(kf, lock);
1078 * camel_key_file_write:
1084 * Write a new list of records to the key file.
1086 * Return value: -1 on io error. The key file will remain unchanged.
1089 camel_key_file_write(CamelKeyFile *kf, camel_block_t *parent, size_t len, camel_key_t *records)
1095 d(printf("write key %08x len = %d\n", *parent, len));
1098 d(printf(" new parent = %08x\n", *parent));
1103 if (key_file_use(kf) == -1)
1108 /* FIXME: Use io util functions? */
1110 fseek(kf->fp, kf->last, SEEK_SET);
1111 fwrite(parent, sizeof(*parent), 1, kf->fp);
1112 fwrite(&size, sizeof(size), 1, kf->fp);
1113 fwrite(records, sizeof(records[0]), len, kf->fp);
1115 if (ferror(kf->fp)) {
1118 kf->last = ftell(kf->fp);
1126 d(printf(" new parent = %08x\n", *parent));
1132 * camel_key_file_read:
1134 * @start: The record pointer. This will be set to the next record pointer on success.
1135 * @len: Number of records read, if != NULL.
1136 * @records: Records, allocated, must be freed with g_free, if != NULL.
1138 * Read the next block of data from the key file. Returns the number of
1141 * Return value: -1 on io error.
1144 camel_key_file_read(CamelKeyFile *kf, camel_block_t *start, size_t *len, camel_key_t **records)
1155 if (key_file_use(kf) == -1)
1158 if (fseek(kf->fp, pos, SEEK_SET) == -1
1159 || fread(&next, sizeof(next), 1, kf->fp) != 1
1160 || fread(&size, sizeof(size), 1, kf->fp) != 1
1170 camel_key_t *keys = g_malloc(size * sizeof(camel_key_t));
1172 if (fread(keys, sizeof(camel_key_t), size, kf->fp) != size) {