1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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.
32 #include <sys/types.h>
34 #include <glib/gstdio.h>
36 #include "camel-block-file.h"
37 #include "camel-file-utils.h"
39 #define d(x) /*(printf("%s(%d):%s: ", __FILE__, __LINE__, __PRETTY_FUNCTION__),(x))*/
41 /* Locks must be obtained in the order defined */
43 struct _CamelBlockFilePrivate {
44 struct _CamelBlockFile *base;
46 GMutex root_lock; /* for modifying the root block */
47 GMutex cache_lock; /* for refcounting, flag manip, cache manip */
48 GMutex io_lock; /* for all io ops */
53 #define CAMEL_BLOCK_FILE_LOCK(kf, lock) (g_mutex_lock(&(kf)->priv->lock))
54 #define CAMEL_BLOCK_FILE_TRYLOCK(kf, lock) (g_mutex_trylock(&(kf)->priv->lock))
55 #define CAMEL_BLOCK_FILE_UNLOCK(kf, lock) (g_mutex_unlock(&(kf)->priv->lock))
57 #define LOCK(x) g_mutex_lock(&x)
58 #define UNLOCK(x) g_mutex_unlock(&x)
60 static GMutex block_file_lock;
62 /* lru cache of block files */
63 static GQueue block_file_list = G_QUEUE_INIT;
64 /* list to store block files that are actually intialised */
65 static GQueue block_file_active_list = G_QUEUE_INIT;
66 static gint block_file_count = 0;
67 static gint block_file_threshhold = 10;
69 static gint sync_nolock (CamelBlockFile *bs);
70 static gint sync_block_nolock (CamelBlockFile *bs, CamelBlock *bl);
72 G_DEFINE_TYPE (CamelBlockFile, camel_block_file, CAMEL_TYPE_OBJECT)
75 block_file_validate_root (CamelBlockFile *bs)
83 retval = fstat (bs->fd, &st);
85 d (printf ("Validate root: '%s'\n", bs->path));
86 d (printf ("version: %.8s (%.8s)\n", bs->root->version, bs->version));
88 "block size: %d (%d)%s\n",
89 br->block_size, bs->block_size,
90 br->block_size != bs->block_size ? " BAD":" OK"));
92 "free: %ld (%d add size < %ld)%s\n",
94 br->free / bs->block_size * bs->block_size,
96 (br->free > st.st_size) ||
97 (br->free % bs->block_size) != 0 ? " BAD":" OK"));
99 "last: %ld (%d and size: %ld)%s\n",
101 br->last / bs->block_size * bs->block_size,
103 (br->last != st.st_size) ||
104 ((br->last % bs->block_size) != 0) ? " BAD": " OK"));
107 (br->flags & CAMEL_BLOCK_FILE_SYNC) ? "SYNC" : "unSYNC"));
110 || memcmp (bs->root->version, bs->version, 8) != 0
111 || br->block_size != bs->block_size
112 || (br->free % bs->block_size) != 0
113 || (br->last % bs->block_size) != 0
115 || st.st_size != br->last
116 || br->free > st.st_size
117 || (br->flags & CAMEL_BLOCK_FILE_SYNC) == 0) {
125 block_file_init_root (CamelBlockFile *bs)
127 CamelBlockRoot *br = bs->root;
129 memset (br, 0, bs->block_size);
130 memcpy (br->version, bs->version, 8);
131 br->last = bs->block_size;
132 br->flags = CAMEL_BLOCK_FILE_SYNC;
134 br->block_size = bs->block_size;
140 block_file_finalize (GObject *object)
142 CamelBlockFile *bs = CAMEL_BLOCK_FILE (object);
146 camel_block_file_sync (bs);
148 /* remove from lru list */
149 LOCK (block_file_lock);
154 /* XXX This is only supposed to be in one block file list
155 * at a time, but not sure if we can guarantee which,
156 * so try removing from both lists. */
157 g_queue_remove (&block_file_list, bs->priv);
158 g_queue_remove (&block_file_active_list, bs->priv);
160 UNLOCK (block_file_lock);
162 while ((bl = g_queue_pop_head (&bs->block_cache)) != NULL) {
163 if (bl->refcount != 0)
164 g_warning ("Block '%u' still referenced", bl->id);
168 g_hash_table_destroy (bs->blocks);
171 camel_block_file_unref_block (bs, bs->root_block);
176 g_mutex_clear (&bs->priv->io_lock);
177 g_mutex_clear (&bs->priv->cache_lock);
178 g_mutex_clear (&bs->priv->root_lock);
182 /* Chain up to parent's finalize() method. */
183 G_OBJECT_CLASS (camel_block_file_parent_class)->finalize (object);
187 camel_block_file_class_init (CamelBlockFileClass *class)
189 GObjectClass *object_class;
191 object_class = G_OBJECT_CLASS (class);
192 object_class->finalize = block_file_finalize;
194 class->validate_root = block_file_validate_root;
195 class->init_root = block_file_init_root;
199 block_hash_func (gconstpointer v)
201 return ((camel_block_t) GPOINTER_TO_UINT (v)) >> CAMEL_BLOCK_SIZE_BITS;
205 camel_block_file_init (CamelBlockFile *bs)
208 bs->block_size = CAMEL_BLOCK_SIZE;
209 g_queue_init (&bs->block_cache);
210 bs->blocks = g_hash_table_new ((GHashFunc) block_hash_func, NULL);
211 /* this cache size and the text index size have been tuned for about the best
212 * with moderate memory usage. Doubling the memory usage barely affects performance. */
213 bs->block_cache_limit = 256;
215 bs->priv = g_malloc0 (sizeof (*bs->priv));
218 g_mutex_init (&bs->priv->root_lock);
219 g_mutex_init (&bs->priv->cache_lock);
220 g_mutex_init (&bs->priv->io_lock);
222 /* link into lru list */
223 LOCK (block_file_lock);
224 g_queue_push_head (&block_file_list, bs->priv);
225 UNLOCK (block_file_lock);
228 /* 'use' a block file for io */
230 block_file_use (CamelBlockFile *bs)
237 * remove file from active list
242 * add it back to end of active list
245 CAMEL_BLOCK_FILE_LOCK (bs, io_lock);
249 else if (bs->priv->deleted) {
250 CAMEL_BLOCK_FILE_UNLOCK (bs, io_lock);
254 d (printf ("Turning block file online: %s\n", bs->path));
257 if ((bs->fd = g_open (bs->path, bs->flags | O_BINARY, 0600)) == -1) {
259 CAMEL_BLOCK_FILE_UNLOCK (bs, io_lock);
264 LOCK (block_file_lock);
266 link = g_queue_find (&block_file_list, bs->priv);
268 g_queue_unlink (&block_file_list, link);
269 g_queue_push_tail_link (&block_file_active_list, link);
274 link = g_queue_peek_head_link (&block_file_list);
276 while (link != NULL && block_file_count > block_file_threshhold) {
277 struct _CamelBlockFilePrivate *nw = link->data;
279 /* We never hit the current blockfile here, as its removed from the list first */
282 /* Need to trylock, as any of these lock levels might be trying
283 * to lock the block_file_lock, so we need to check and abort if so */
284 if (CAMEL_BLOCK_FILE_TRYLOCK (bf, root_lock)) {
285 if (CAMEL_BLOCK_FILE_TRYLOCK (bf, cache_lock)) {
286 if (CAMEL_BLOCK_FILE_TRYLOCK (bf, io_lock)) {
287 d (printf ("[%d] Turning block file offline: %s\n", block_file_count - 1, bf->path));
292 CAMEL_BLOCK_FILE_UNLOCK (bf, io_lock);
294 CAMEL_BLOCK_FILE_UNLOCK (bf, cache_lock);
296 CAMEL_BLOCK_FILE_UNLOCK (bf, root_lock);
300 link = g_list_next (link);
303 UNLOCK (block_file_lock);
309 block_file_unuse (CamelBlockFile *bs)
313 LOCK (block_file_lock);
314 link = g_queue_find (&block_file_active_list, bs->priv);
316 g_queue_unlink (&block_file_active_list, link);
317 g_queue_push_tail_link (&block_file_list, link);
319 UNLOCK (block_file_lock);
321 CAMEL_BLOCK_FILE_UNLOCK (bs, io_lock);
325 * o = camel_cache_get (c, key);
326 * camel_cache_unref (c, key);
327 * camel_cache_add (c, key, o);
328 * camel_cache_remove (c, key);
332 * camel_block_file_new:
337 * Allocate a new block file, stored at @path. @version contains an 8 character
338 * version string which must match the head of the file, or the file will be
341 * @block_size is currently ignored and is set to CAMEL_BLOCK_SIZE.
343 * Returns: The new block file, or NULL if it could not be created.
346 camel_block_file_new (const gchar *path,
348 const gchar version[8],
351 CamelBlockFileClass *class;
354 bs = g_object_new (CAMEL_TYPE_BLOCK_FILE, NULL);
355 memcpy (bs->version, version, 8);
356 bs->path = g_strdup (path);
359 bs->root_block = camel_block_file_get_block (bs, 0);
360 if (bs->root_block == NULL) {
364 camel_block_file_detach_block (bs, bs->root_block);
365 bs->root = (CamelBlockRoot *) &bs->root_block->data;
367 /* we only need these flags on first open */
368 bs->flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
370 class = CAMEL_BLOCK_FILE_GET_CLASS (bs);
372 /* Do we need to init the root block? */
373 if (class->validate_root (bs) == -1) {
374 d (printf ("Initialise root block: %.8s\n", version));
376 class->init_root (bs);
377 camel_block_file_touch_block (bs, bs->root_block);
378 if (block_file_use (bs) == -1) {
382 if (sync_block_nolock (bs, bs->root_block) == -1
383 || ftruncate (bs->fd, bs->root->last) == -1) {
384 block_file_unuse (bs);
388 block_file_unuse (bs);
395 camel_block_file_rename (CamelBlockFile *bs,
402 g_return_val_if_fail (CAMEL_IS_BLOCK_FILE (bs), -1);
403 g_return_val_if_fail (path != NULL, -1);
405 CAMEL_BLOCK_FILE_LOCK (bs, io_lock);
407 ret = g_rename (bs->path, path);
409 /* Maybe the rename actually worked */
411 if (g_stat (path, &st) == 0
412 && g_stat (bs->path, &st) == -1
420 bs->path = g_strdup (path);
423 CAMEL_BLOCK_FILE_UNLOCK (bs, io_lock);
429 camel_block_file_delete (CamelBlockFile *bs)
433 g_return_val_if_fail (CAMEL_IS_BLOCK_FILE (bs), -1);
435 CAMEL_BLOCK_FILE_LOCK (bs, io_lock);
438 LOCK (block_file_lock);
440 UNLOCK (block_file_lock);
445 bs->priv->deleted = TRUE;
446 ret = g_unlink (bs->path);
448 CAMEL_BLOCK_FILE_UNLOCK (bs, io_lock);
455 * camel_block_file_new_block:
458 * Allocate a new block, return a pointer to it. Old blocks
459 * may be flushed to disk during this call.
461 * Returns: The block, or NULL if an error occurred.
464 camel_block_file_new_block (CamelBlockFile *bs)
468 g_return_val_if_fail (CAMEL_IS_BLOCK_FILE (bs), NULL);
470 CAMEL_BLOCK_FILE_LOCK (bs, root_lock);
472 if (bs->root->free) {
473 bl = camel_block_file_get_block (bs, bs->root->free);
476 bs->root->free = ((camel_block_t *) bl->data)[0];
478 bl = camel_block_file_get_block (bs, bs->root->last);
481 bs->root->last += CAMEL_BLOCK_SIZE;
484 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
486 bl->flags |= CAMEL_BLOCK_DIRTY;
487 memset (bl->data, 0, CAMEL_BLOCK_SIZE);
489 CAMEL_BLOCK_FILE_UNLOCK (bs, root_lock);
495 * camel_block_file_free_block:
502 camel_block_file_free_block (CamelBlockFile *bs,
507 g_return_val_if_fail (CAMEL_IS_BLOCK_FILE (bs), -1);
509 bl = camel_block_file_get_block (bs, id);
513 CAMEL_BLOCK_FILE_LOCK (bs, root_lock);
515 ((camel_block_t *) bl->data)[0] = bs->root->free;
516 bs->root->free = bl->id;
517 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
518 bl->flags |= CAMEL_BLOCK_DIRTY;
519 camel_block_file_unref_block (bs, bl);
521 CAMEL_BLOCK_FILE_UNLOCK (bs, root_lock);
527 * camel_block_file_get_block:
531 * Retreive a block @id.
533 * Returns: The block, or NULL if blockid is invalid or a file error
537 camel_block_file_get_block (CamelBlockFile *bs,
542 g_return_val_if_fail (CAMEL_IS_BLOCK_FILE (bs), NULL);
544 /* Sanity check: Dont allow reading of root block (except before its been read)
545 * or blocks with invalid block id's */
546 if ((bs->root == NULL && id != 0)
547 || (bs->root != NULL && (id > bs->root->last || id == 0))
548 || (id % bs->block_size) != 0) {
553 CAMEL_BLOCK_FILE_LOCK (bs, cache_lock);
555 bl = g_hash_table_lookup (bs->blocks, GUINT_TO_POINTER (id));
557 d (printf ("Get block %08x: %s\n", id, bl?"cached":"must read"));
560 GQueue trash = G_QUEUE_INIT;
564 if (block_file_use (bs) == -1) {
565 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
569 bl = g_malloc0 (sizeof (*bl));
571 if (lseek (bs->fd, id, SEEK_SET) == -1 ||
572 camel_read (bs->fd, (gchar *) bl->data, CAMEL_BLOCK_SIZE, NULL, NULL) == -1) {
573 block_file_unuse (bs);
574 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
579 bs->block_cache_count++;
580 g_hash_table_insert (bs->blocks, GUINT_TO_POINTER (bl->id), bl);
582 /* flush old blocks */
583 link = g_queue_peek_tail_link (&bs->block_cache);
585 while (link != NULL && bs->block_cache_count > bs->block_cache_limit) {
586 CamelBlock *flush = link->data;
588 if (flush->refcount == 0) {
589 if (sync_block_nolock (bs, flush) != -1) {
590 g_hash_table_remove (bs->blocks, GUINT_TO_POINTER (flush->id));
591 g_queue_push_tail (&trash, link);
594 bs->block_cache_count--;
598 link = g_list_previous (link);
601 /* Remove deleted blocks from the cache. */
602 while ((link = g_queue_pop_head (&trash)) != NULL)
603 g_queue_delete_link (&bs->block_cache, link);
606 block_file_unuse (bs);
608 g_queue_remove (&bs->block_cache, bl);
611 g_queue_push_head (&bs->block_cache, bl);
614 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
616 d (printf ("Got block %08x\n", id));
622 * camel_block_file_detach_block:
626 * Detatch a block from the block file's cache. The block should
627 * be unref'd or attached when finished with. The block file will
628 * perform no writes of this block or flushing of it if the cache
632 camel_block_file_detach_block (CamelBlockFile *bs,
635 g_return_if_fail (CAMEL_IS_BLOCK_FILE (bs));
636 g_return_if_fail (bl != NULL);
638 CAMEL_BLOCK_FILE_LOCK (bs, cache_lock);
640 g_hash_table_remove (bs->blocks, GUINT_TO_POINTER (bl->id));
641 g_queue_remove (&bs->block_cache, bl);
642 bl->flags |= CAMEL_BLOCK_DETACHED;
644 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
648 * camel_block_file_attach_block:
652 * Reattach a block that has been detached.
655 camel_block_file_attach_block (CamelBlockFile *bs,
658 g_return_if_fail (CAMEL_IS_BLOCK_FILE (bs));
659 g_return_if_fail (bl != NULL);
661 CAMEL_BLOCK_FILE_LOCK (bs, cache_lock);
663 g_hash_table_insert (bs->blocks, GUINT_TO_POINTER (bl->id), bl);
664 g_queue_push_tail (&bs->block_cache, bl);
665 bl->flags &= ~CAMEL_BLOCK_DETACHED;
667 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
671 * camel_block_file_touch_block:
675 * Mark a block as dirty. The block will be written to disk if
676 * it ever expires from the cache.
679 camel_block_file_touch_block (CamelBlockFile *bs,
682 g_return_if_fail (CAMEL_IS_BLOCK_FILE (bs));
683 g_return_if_fail (bl != NULL);
685 CAMEL_BLOCK_FILE_LOCK (bs, root_lock);
686 CAMEL_BLOCK_FILE_LOCK (bs, cache_lock);
688 bl->flags |= CAMEL_BLOCK_DIRTY;
690 if ((bs->root->flags & CAMEL_BLOCK_FILE_SYNC) && bl != bs->root_block) {
691 d (printf ("turning off sync flag\n"));
692 bs->root->flags &= ~CAMEL_BLOCK_FILE_SYNC;
693 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
694 camel_block_file_sync_block (bs, bs->root_block);
697 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
698 CAMEL_BLOCK_FILE_UNLOCK (bs, root_lock);
702 * camel_block_file_unref_block:
706 * Mark a block as unused. If a block is used it will not be
707 * written to disk, or flushed from memory.
709 * If a block is detatched and this is the last reference, the
710 * block will be freed.
713 camel_block_file_unref_block (CamelBlockFile *bs,
716 g_return_if_fail (CAMEL_IS_BLOCK_FILE (bs));
717 g_return_if_fail (bl != NULL);
719 CAMEL_BLOCK_FILE_LOCK (bs, cache_lock);
721 if (bl->refcount == 1 && (bl->flags & CAMEL_BLOCK_DETACHED))
726 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
730 sync_block_nolock (CamelBlockFile *bs,
733 d (printf ("Sync block %08x: %s\n", bl->id, (bl->flags & CAMEL_BLOCK_DIRTY)?"dirty":"clean"));
735 if (bl->flags & CAMEL_BLOCK_DIRTY) {
736 if (lseek (bs->fd, bl->id, SEEK_SET) == -1
737 || write (bs->fd, bl->data, CAMEL_BLOCK_SIZE) != CAMEL_BLOCK_SIZE) {
740 bl->flags &= ~CAMEL_BLOCK_DIRTY;
747 sync_nolock (CamelBlockFile *bs)
752 head = g_queue_peek_head_link (&bs->block_cache);
754 for (link = head; link != NULL; link = g_list_next (link)) {
755 CamelBlock *bl = link->data;
757 if (bl->flags & CAMEL_BLOCK_DIRTY) {
759 if (sync_block_nolock (bs, bl) == -1)
765 && (bs->root_block->flags & CAMEL_BLOCK_DIRTY) == 0
766 && (bs->root->flags & CAMEL_BLOCK_FILE_SYNC) != 0)
769 d (printf ("turning on sync flag\n"));
771 bs->root->flags |= CAMEL_BLOCK_FILE_SYNC;
772 bs->root_block->flags |= CAMEL_BLOCK_DIRTY;
774 return sync_block_nolock (bs, bs->root_block);
778 * camel_block_file_sync_block:
782 * Flush a block to disk immediately. The block will only
783 * be flushed to disk if it is marked as dirty (touched).
785 * Returns: -1 on io error.
788 camel_block_file_sync_block (CamelBlockFile *bs,
793 g_return_val_if_fail (CAMEL_IS_BLOCK_FILE (bs), -1);
794 g_return_val_if_fail (bl != NULL, -1);
797 if (block_file_use (bs) == -1)
800 ret = sync_block_nolock (bs, bl);
802 block_file_unuse (bs);
808 * camel_block_file_sync:
811 * Sync all dirty blocks to disk, including the root block.
813 * Returns: -1 on io error.
816 camel_block_file_sync (CamelBlockFile *bs)
820 g_return_val_if_fail (CAMEL_IS_BLOCK_FILE (bs), -1);
822 CAMEL_BLOCK_FILE_LOCK (bs, root_lock);
823 CAMEL_BLOCK_FILE_LOCK (bs, cache_lock);
826 if (block_file_use (bs) == -1)
829 ret = sync_nolock (bs);
830 block_file_unuse (bs);
833 CAMEL_BLOCK_FILE_UNLOCK (bs, cache_lock);
834 CAMEL_BLOCK_FILE_UNLOCK (bs, root_lock);
839 /* ********************************************************************** */
841 struct _CamelKeyFilePrivate {
842 struct _CamelKeyFile *base;
847 #define CAMEL_KEY_FILE_LOCK(kf, lock) (g_mutex_lock(&(kf)->priv->lock))
848 #define CAMEL_KEY_FILE_TRYLOCK(kf, lock) (g_mutex_trylock(&(kf)->priv->lock))
849 #define CAMEL_KEY_FILE_UNLOCK(kf, lock) (g_mutex_unlock(&(kf)->priv->lock))
851 static GMutex key_file_lock;
853 /* lru cache of block files */
854 static GQueue key_file_list = G_QUEUE_INIT;
855 static GQueue key_file_active_list = G_QUEUE_INIT;
856 static gint key_file_count = 0;
857 static const gint key_file_threshhold = 10;
859 G_DEFINE_TYPE (CamelKeyFile, camel_key_file, CAMEL_TYPE_OBJECT)
862 key_file_finalize (GObject *object)
864 CamelKeyFile *bs = CAMEL_KEY_FILE (object);
866 LOCK (key_file_lock);
868 /* XXX This is only supposed to be in one key file list
869 * at a time, but not sure if we can guarantee which,
870 * so try removing from both lists. */
871 g_queue_remove (&key_file_list, bs->priv);
872 g_queue_remove (&key_file_active_list, bs->priv);
879 UNLOCK (key_file_lock);
883 g_mutex_clear (&bs->priv->lock);
887 /* Chain up to parent's finalize() method. */
888 G_OBJECT_CLASS (camel_key_file_parent_class)->finalize (object);
892 camel_key_file_class_init (CamelKeyFileClass *class)
894 GObjectClass *object_class;
896 object_class = G_OBJECT_CLASS (class);
897 object_class->finalize = key_file_finalize;
901 camel_key_file_init (CamelKeyFile *bs)
903 bs->priv = g_malloc0 (sizeof (*bs->priv));
906 g_mutex_init (&bs->priv->lock);
908 LOCK (key_file_lock);
909 g_queue_push_head (&key_file_list, bs->priv);
910 UNLOCK (key_file_lock);
913 /* 'use' a key file for io */
915 key_file_use (CamelKeyFile *bs)
923 * remove file from active list
928 * add it back to end of active list
931 /* TODO: Check header on reset? */
933 CAMEL_KEY_FILE_LOCK (bs, lock);
937 else if (bs->priv->deleted) {
938 CAMEL_KEY_FILE_UNLOCK (bs, lock);
942 d (printf ("Turning key file online: '%s'\n", bs->path));
945 if ((bs->flags & O_ACCMODE) == O_RDONLY)
950 if ((fd = g_open (bs->path, bs->flags | O_BINARY, 0600)) == -1
951 || (bs->fp = fdopen (fd, flag)) == NULL) {
955 CAMEL_KEY_FILE_UNLOCK (bs, lock);
960 LOCK (key_file_lock);
962 link = g_queue_find (&key_file_list, bs->priv);
964 g_queue_unlink (&key_file_list, link);
965 g_queue_push_tail_link (&key_file_active_list, link);
970 link = g_queue_peek_head_link (&key_file_list);
971 while (link != NULL && key_file_count > key_file_threshhold) {
972 struct _CamelKeyFilePrivate *nw = link->data;
974 /* We never hit the current keyfile here, as its removed from the list first */
976 if (bf->fp != NULL) {
977 /* Need to trylock, as any of these lock levels might be trying
978 * to lock the key_file_lock, so we need to check and abort if so */
979 if (CAMEL_BLOCK_FILE_TRYLOCK (bf, lock)) {
980 d (printf ("Turning key file offline: %s\n", bf->path));
984 CAMEL_BLOCK_FILE_UNLOCK (bf, lock);
988 link = g_list_next (link);
991 UNLOCK (key_file_lock);
997 key_file_unuse (CamelKeyFile *bs)
1001 LOCK (key_file_lock);
1002 link = g_queue_find (&key_file_active_list, bs->priv);
1004 g_queue_unlink (&key_file_active_list, link);
1005 g_queue_push_tail_link (&key_file_list, link);
1007 UNLOCK (key_file_lock);
1009 CAMEL_KEY_FILE_UNLOCK (bs, lock);
1013 * camel_key_file_new:
1015 * @flags: open flags
1016 * @version: Version string (header) of file. Currently
1017 * written but not checked.
1019 * Create a new key file. A linked list of record blocks.
1021 * Returns: A new key file, or NULL if the file could not
1022 * be opened/created/initialised.
1025 camel_key_file_new (const gchar *path,
1027 const gchar version[8])
1033 d (printf ("New key file '%s'\n", path));
1035 kf = g_object_new (CAMEL_TYPE_KEY_FILE, NULL);
1036 kf->path = g_strdup (path);
1041 if (key_file_use (kf) == -1) {
1042 g_object_unref (kf);
1045 fseek (kf->fp, 0, SEEK_END);
1046 last = ftell (kf->fp);
1048 fwrite (version, 8, 1, kf->fp);
1053 err = ferror (kf->fp);
1054 key_file_unuse (kf);
1056 /* we only need these flags on first open */
1057 kf->flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
1060 g_object_unref (kf);
1069 camel_key_file_rename (CamelKeyFile *kf,
1076 g_return_val_if_fail (CAMEL_IS_KEY_FILE (kf), -1);
1077 g_return_val_if_fail (path != NULL, -1);
1079 CAMEL_KEY_FILE_LOCK (kf, lock);
1081 ret = g_rename (kf->path, path);
1083 /* Maybe the rename actually worked */
1085 if (g_stat (path, &st) == 0
1086 && g_stat (kf->path, &st) == -1
1094 kf->path = g_strdup (path);
1097 CAMEL_KEY_FILE_UNLOCK (kf, lock);
1103 camel_key_file_delete (CamelKeyFile *kf)
1107 g_return_val_if_fail (CAMEL_IS_KEY_FILE (kf), -1);
1109 CAMEL_KEY_FILE_LOCK (kf, lock);
1112 LOCK (key_file_lock);
1114 UNLOCK (key_file_lock);
1119 kf->priv->deleted = TRUE;
1120 ret = g_unlink (kf->path);
1122 CAMEL_KEY_FILE_UNLOCK (kf, lock);
1129 * camel_key_file_write:
1135 * Write a new list of records to the key file.
1137 * Returns: -1 on io error. The key file will remain unchanged.
1140 camel_key_file_write (CamelKeyFile *kf,
1141 camel_block_t *parent,
1143 camel_key_t *records)
1149 g_return_val_if_fail (CAMEL_IS_KEY_FILE (kf), -1);
1150 g_return_val_if_fail (parent != NULL, -1);
1151 g_return_val_if_fail (records != NULL, -1);
1153 d (printf ("write key %08x len = %d\n", *parent, len));
1156 d (printf (" new parent = %08x\n", *parent));
1161 if (key_file_use (kf) == -1)
1166 /* FIXME: Use io util functions? */
1168 fseek (kf->fp, kf->last, SEEK_SET);
1169 fwrite (parent, sizeof (*parent), 1, kf->fp);
1170 fwrite (&size, sizeof (size), 1, kf->fp);
1171 fwrite (records, sizeof (records[0]), len, kf->fp);
1173 if (ferror (kf->fp)) {
1176 kf->last = ftell (kf->fp);
1182 key_file_unuse (kf);
1184 d (printf (" new parent = %08x\n", *parent));
1190 * camel_key_file_read:
1192 * @start: The record pointer. This will be set to the next record pointer on success.
1193 * @len: Number of records read, if != NULL.
1194 * @records: Records, allocated, must be freed with g_free, if != NULL.
1196 * Read the next block of data from the key file. Returns the number of
1199 * Returns: -1 on io error.
1202 camel_key_file_read (CamelKeyFile *kf,
1203 camel_block_t *start,
1205 camel_key_t **records)
1212 g_return_val_if_fail (CAMEL_IS_KEY_FILE (kf), -1);
1213 g_return_val_if_fail (start != NULL, -1);
1220 if (key_file_use (kf) == -1)
1223 if (fseek (kf->fp, pos, SEEK_SET) == -1
1224 || fread (&next, sizeof (next), 1, kf->fp) != 1
1225 || fread (&size, sizeof (size), 1, kf->fp) != 1
1235 camel_key_t *keys = g_malloc (size * sizeof (camel_key_t));
1237 if (fread (keys, sizeof (camel_key_t), size, kf->fp) != size) {
1249 key_file_unuse (kf);