]> git.proxmox.com Git - pve-cluster.git/blame - data/src/memdb.c
cleanup format strings for cfs_* messages
[pve-cluster.git] / data / src / memdb.c
CommitLineData
fe000966
DM
1/*
2 Copyright (C) 2010 Proxmox Server Solutions GmbH
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Affero General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Affero General Public License for more details.
13
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 Author: Dietmar Maurer <dietmar@proxmox.com>
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif /* HAVE_CONFIG_H */
24
25#include <stdio.h>
e5a5a3ea 26#include <inttypes.h>
fe000966
DM
27#include <stdlib.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include <sys/file.h>
32#include <unistd.h>
33#include <dirent.h>
34#include <string.h>
35#include <errno.h>
36#include <glib.h>
37
38#include "cfs-utils.h"
39#include "memdb.h"
40#include "status.h"
41
42#define CFS_LOCK_TIMEOUT (60*2)
43
44memdb_tree_entry_t *
45memdb_tree_entry_new(
46 const char *name)
47{
48 g_return_val_if_fail(name != NULL, NULL);
49
50 memdb_tree_entry_t *te = g_malloc0(sizeof(memdb_tree_entry_t) + strlen(name) + 1);
51 g_return_val_if_fail(te != NULL, NULL);
52
53 strcpy(te->name, name);
54
55 return te;
56}
57
58memdb_tree_entry_t *
59memdb_tree_entry_copy(
60 memdb_tree_entry_t *te,
61 gboolean with_data)
62{
63 g_return_val_if_fail(te != NULL, NULL);
64
65 memdb_tree_entry_t *cpy = memdb_tree_entry_new(te->name);
66
67 cpy->parent = te->parent;
68 cpy->inode = te->inode;
69 cpy->version = te->version;
70 cpy->writer = te->writer;
71 cpy->mtime = te->mtime;
72 cpy->type = te->type;
73 cpy->size = te->size;
74
75 if (with_data && te->size && te->type == DT_REG) {
76 cpy->data.value = g_memdup(te->data.value, te->size);
77 } else {
78 cpy->data.value = NULL;
79 }
80
81 return cpy;
82}
83
84void
85memdb_tree_entry_free(
86 memdb_tree_entry_t *te)
87{
88 g_return_if_fail(te != NULL);
89
90 if (te->type == DT_REG) {
91 if (te->data.value)
92 g_free(te->data.value);
93 }
94
95 if (te->type == DT_DIR) {
96 if (te->data.entries)
97 g_hash_table_destroy(te->data.entries);
98 }
99
100 g_free(te);
101}
102
103void
104memdb_lock_info_free(memdb_lock_info_t *li)
105{
106 g_return_if_fail(li != NULL);
107
108 if (li->path)
109 g_free(li->path);
110
111 g_free(li);
112}
113
114static gint
115memdb_tree_compare(
116 gconstpointer v1,
117 gconstpointer v2)
118{
119 guint64 a = ((const memdb_tree_entry_t *)v1)->inode;
120 guint64 b = ((const memdb_tree_entry_t *)v2)->inode;
121
122 if (a == b)
123 return 0;
124
125 if (a > b)
126 return 1;
127
128 return -1;
129}
130
131static void
132split_path(
133 const char *path,
134 char **dirname,
135 char **basename)
136{
137 char *dup = g_strdup (path);
138 int len = strlen (dup) - 1;
139 while (len >= 0 && dup[len] == '/') dup[len--] = 0;
140
141 char *dn = g_path_get_dirname (dup);
142 char *bn = g_path_get_basename (dup);
143
144 g_free (dup);
145
146 *dirname = dn;
147 *basename = bn;
148}
149
150static memdb_tree_entry_t *
151memdb_lookup_dir_entry(
152 memdb_t *memdb,
153 const char *name,
154 memdb_tree_entry_t *parent)
155{
156
157 g_return_val_if_fail(memdb != NULL, NULL);
158 g_return_val_if_fail(name != NULL, NULL);
159 g_return_val_if_fail(parent != NULL, NULL);
160 g_return_val_if_fail(parent->type == DT_DIR, NULL);
161
162 GHashTable *ht = parent->data.entries;
163
164 g_return_val_if_fail(ht != NULL, NULL);
165
166 return g_hash_table_lookup(ht, name);
167}
168
169static memdb_tree_entry_t *
170memdb_lookup_path(
171 memdb_t *memdb,
172 const char *path,
173 memdb_tree_entry_t **parent)
174{
175 g_return_val_if_fail(memdb != NULL, NULL);
176 g_return_val_if_fail(path != NULL, NULL);
177 g_return_val_if_fail(parent != NULL, NULL);
178
179 memdb_tree_entry_t *cdir = memdb->root;
180 *parent = NULL;
181
182 if (path[0] == 0 || ((path[0] == '.' || path[0] == '/') && path[1] == 0))
183 return cdir;
184
185 gchar **set = g_strsplit_set(path, "/", 0);
186
187 int i = 0;
188 char *name;
189
190 while ((name = set[i++])) {
191
192 if (name[0] == 0) continue;
193
194 *parent = cdir;
195 if ((cdir = memdb_lookup_dir_entry(memdb, name, cdir)) == NULL)
196 break;
197 }
198
199 g_strfreev(set);
200
201 return cdir;
202}
203
204
205static gboolean
206name_is_vm_config(
207 const char *name,
208 guint32 *vmid_ret)
209{
210 if (!name || name[0] < '1' || name[0] > '9')
211 return FALSE;
212
8f8fbe90 213
fe000966 214 char *end = NULL;
8f8fbe90
DM
215
216 errno = 0; /* see man strtoul */
217
fe000966
DM
218 guint32 vmid = strtoul(name, &end, 10);
219
220 if (!end || end[0] != '.' || end[1] != 'c'|| end[2] != 'o' || end[3] != 'n' ||
8f8fbe90 221 end[4] != 'f' || end[5] != 0 || errno != 0)
fe000966
DM
222 return FALSE;
223
224 if (vmid_ret)
225 *vmid_ret = vmid;
226
227 return TRUE;
228}
229
230static gboolean
231valid_nodename(
232 const char *nodename)
233{
234 g_return_val_if_fail(nodename != NULL, FALSE);
235
236 /* LDH rule (letters, digits, hyphen) */
237
238 int len = strlen(nodename);
ba2e1b39
DM
239
240 if (len < 1) {
241 return FALSE;
242 }
243
fe000966
DM
244 for (int i = 0; i < len; i ++) {
245 char c = nodename[i];
246 if ((c >= 'A' && c <= 'Z') ||
247 (c >= 'a' && c <= 'z') ||
ba2e1b39 248 (c >= '0' && c <= '9') ||
fe000966
DM
249 (i != 0 && i != (len-1) && c == '-'))
250 continue;
251 return FALSE;
252 }
253
254 return TRUE;
255}
256
257static char*
258dir_contain_vm_config(
259 const char *dirname,
260 int *vmtype_ret)
261{
262 if (!dirname)
263 return NULL;
264
265 if (strncmp(dirname, "nodes/", 6) != 0)
266 return NULL;
267
268 dirname += 6;
269
270 char *nodename = NULL;
271
272 char **sa = g_strsplit(dirname, "/", 2);
273 if (sa[0] && sa[1] && valid_nodename(sa[0])) {
274 if (strcmp(sa[1], "qemu-server") == 0) {
275 *vmtype_ret = VMTYPE_QEMU;
276 nodename = g_strdup(sa[0]);
277 } else if (strcmp(sa[1], "openvz") == 0) {
278 *vmtype_ret = VMTYPE_OPENVZ;
279 nodename = g_strdup(sa[0]);
7f66b436
DM
280 } else if (strcmp(sa[1], "lxc") == 0) {
281 *vmtype_ret = VMTYPE_LXC;
282 nodename = g_strdup(sa[0]);
fe000966
DM
283 }
284 }
285
286 g_strfreev(sa);
287
288 return nodename;
289}
290
291static char *
292path_contain_vm_config(
293 const char *path,
294 int *vmtype_ret,
295 guint32 *vmid_ret)
296{
297 if (!path)
298 return NULL;
299
300 char *dirname = NULL;
301 char *base = NULL;
302 char *nodename = NULL;
303
304 split_path(path, &dirname, &base);
305
6e73d5c2
DM
306 if (name_is_vm_config(base, vmid_ret))
307 nodename = dir_contain_vm_config(dirname, vmtype_ret);
7f66b436 308
fe000966
DM
309 if (dirname) g_free (dirname);
310 if (base) g_free (base);
311
312 return nodename;
313}
314
315static gboolean
316vmlist_add_dir(
317 memdb_t *memdb,
318 GHashTable *vmlist,
319 const char *nodename,
320 const int vmtype,
321 memdb_tree_entry_t *subdir)
322{
323 g_return_val_if_fail(memdb != NULL, FALSE);
324 g_return_val_if_fail(vmlist != NULL, FALSE);
325 g_return_val_if_fail(subdir != NULL, FALSE);
326 g_return_val_if_fail(subdir->type == DT_DIR, FALSE);
327 g_return_val_if_fail(subdir->data.entries != NULL, FALSE);
328
329 gboolean ret = TRUE;
330
331 GHashTable *ht = subdir->data.entries;
332 GHashTableIter iter;
333 gpointer key, value;
334
335 g_hash_table_iter_init (&iter, ht);
336
337 while (g_hash_table_iter_next (&iter, &key, &value)) {
338
339 memdb_tree_entry_t *node_te = (memdb_tree_entry_t *)value;
340
6e73d5c2
DM
341 if (node_te->type != DT_REG)
342 continue;
7f66b436 343
6e73d5c2
DM
344 guint32 vmid = 0;
345 if (!name_is_vm_config(node_te->name, &vmid))
346 continue;
7f66b436 347
6e73d5c2
DM
348 if (!vmlist_hash_insert_vm(vmlist, vmtype, vmid, nodename, FALSE))
349 ret = FALSE;
fe000966
DM
350 }
351
352 return ret;
353}
354
355
356gboolean
357memdb_lock_expired(
358 memdb_t *memdb,
359 const char *path,
360 const guchar csum[32])
361{
362 g_return_val_if_fail(memdb != NULL, FALSE);
363 g_return_val_if_fail(memdb->locks != NULL, FALSE);
364 g_return_val_if_fail(path != NULL, FALSE);
365 g_return_val_if_fail(csum != NULL, FALSE);
366
367 memdb_lock_info_t *li;
368 uint32_t ctime = time(NULL);
369
370 if ((li = g_hash_table_lookup(memdb->locks, path))) {
371 if (memcmp(csum, li->csum, 32) != 0) {
372 li->ltime = ctime;
373 memcpy(li->csum, csum, 32);
374 g_critical("wrong lock csum - reset timeout");
375 return FALSE;
376 }
377 if ((ctime > li->ltime) && ((ctime - li->ltime) > CFS_LOCK_TIMEOUT))
378 return TRUE;
379 } else {
380 li = g_new0(memdb_lock_info_t, 1);
381 li->path = g_strdup(path);
382 li->ltime = ctime;
383 memcpy(li->csum, csum, 32);
384 g_hash_table_replace(memdb->locks, li->path, li);
385 }
386
387 return FALSE;
388}
389
390void
391memdb_update_locks(memdb_t *memdb)
392{
393 g_return_if_fail(memdb != NULL);
394 g_return_if_fail(memdb->locks != NULL);
395
396 memdb_tree_entry_t *te, *parent;
397
398 if (!(te = memdb_lookup_path(memdb, "priv/lock", &parent)))
399 return;
400
401 if (te->type != DT_DIR)
402 return;
403
404
405 GHashTable *old = memdb->locks;
406 memdb->locks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
407 (GDestroyNotify)memdb_lock_info_free);
408 GHashTableIter iter;
409 GHashTable *ht = te->data.entries;
410
411 gpointer key, value;
412
413 g_hash_table_iter_init (&iter, ht);
414 while (g_hash_table_iter_next (&iter, &key, &value)) {
415
416 memdb_tree_entry_t *lock_te = (memdb_tree_entry_t *)value;
417 if (lock_te->type != DT_DIR)
418 continue;
419
420 memdb_lock_info_t *li;
421 li = g_new0(memdb_lock_info_t, 1);
422 li->path = g_strdup_printf("priv/lock/%s", lock_te->name);
423
424 guchar csum[32];
425 if (memdb_tree_entry_csum(lock_te, csum)) {
426 memcpy(li->csum, csum, 32);
427 memdb_lock_info_t *oldli;
428 if ((oldli = g_hash_table_lookup(memdb->locks, lock_te->name)) &&
429 (memcmp(csum, oldli->csum, 32) == 0)) {
430 li->ltime = oldli->ltime;
431 } else {
432 li->ltime = time(NULL);
433 }
434 g_hash_table_insert(memdb->locks, li->path, li);
435 } else {
436 memdb_lock_info_free(li);
437 }
438 }
439
440 if (old)
441 g_hash_table_destroy(old);
442
443}
444
445gboolean
446memdb_recreate_vmlist(
447 memdb_t *memdb)
448{
449 g_return_val_if_fail(memdb != NULL, FALSE);
450
451 memdb_tree_entry_t *te, *parent;
452
453 if (!(te = memdb_lookup_path(memdb, "nodes", &parent)))
454 return TRUE;
455
456 if (te->type != DT_DIR)
457 return TRUE;
458
459 GHashTable *vmlist = vmlist_hash_new();
460
461 GHashTable *ht = te->data.entries;
462
463 gboolean ret = TRUE;
464
465 GHashTableIter iter;
466 gpointer key, value;
467
468 g_hash_table_iter_init (&iter, ht);
469
470 while (g_hash_table_iter_next (&iter, &key, &value)) {
471
472 memdb_tree_entry_t *node_te = (memdb_tree_entry_t *)value;
473 if (node_te->type != DT_DIR)
474 continue;
475
476 if (!valid_nodename(node_te->name))
477 continue;
478
479 if ((te = g_hash_table_lookup(node_te->data.entries, "qemu-server"))) {
480 if (!vmlist_add_dir(memdb, vmlist, node_te->name, VMTYPE_QEMU, te))
481 ret = FALSE;
482 }
483 if ((te = g_hash_table_lookup(node_te->data.entries, "openvz"))) {
484 if (!vmlist_add_dir(memdb, vmlist, node_te->name, VMTYPE_OPENVZ, te))
485 ret = FALSE;
486 }
7f66b436
DM
487 if ((te = g_hash_table_lookup(node_te->data.entries, "lxc"))) {
488 if (!vmlist_add_dir(memdb, vmlist, node_te->name, VMTYPE_LXC, te))
489 ret = FALSE;
490 }
fe000966
DM
491 }
492
493 /* always update list - even if we detected duplicates */
494 cfs_status_set_vmlist(vmlist);
495
496 return ret;
497}
498
499memdb_t *
500memdb_open(const char *dbfilename)
501{
502 memdb_t *memdb = g_new0(memdb_t, 1);
503
89fde9ac 504 g_mutex_init(&memdb->mutex);
fe000966
DM
505
506 memdb->dbfilename = g_strdup(dbfilename);
507
508 memdb->root = memdb_tree_entry_new("");
509 memdb->root->data.entries = g_hash_table_new(g_str_hash, g_str_equal);
510 memdb->root->type = DT_DIR;
511
512 memdb->index = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL,
513 (GDestroyNotify)memdb_tree_entry_free);
514
515 g_hash_table_replace(memdb->index, &memdb->root->inode, memdb->root);
516
517 memdb->locks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
518 (GDestroyNotify)memdb_lock_info_free);
519
520 if (!(memdb->bdb = bdb_backend_open(dbfilename, memdb->root, memdb->index))) {
521 memdb_close(memdb);
522 return NULL;
523 }
524
525 record_memdb_reload();
526
527 if (!memdb_recreate_vmlist(memdb)) {
528 memdb_close(memdb);
529 return NULL;
530 }
531
532 memdb_update_locks(memdb);
533
e5a5a3ea 534 cfs_debug("memdb open '%s' successful (version = %016" PRIX64 ")",
fe000966
DM
535 dbfilename, memdb->root->version);
536
537 return memdb;
538}
539
540void
541memdb_close(memdb_t *memdb)
542{
543 g_return_if_fail(memdb != NULL);
544
89fde9ac 545 g_mutex_lock (&memdb->mutex);
fe000966
DM
546
547 if (memdb->bdb)
548 bdb_backend_close(memdb->bdb);
549
550 if (memdb->index)
551 g_hash_table_destroy(memdb->index);
552
553 if (memdb->locks)
554 g_hash_table_destroy(memdb->locks);
555
556 if (memdb->dbfilename)
557 g_free(memdb->dbfilename);
558
559 memdb->index = NULL;
560 memdb->bdb = NULL;
561 memdb->dbfilename = NULL;
562
89fde9ac 563 g_mutex_unlock (&memdb->mutex);
fe000966 564
89fde9ac 565 g_mutex_clear (&memdb->mutex);
fe000966
DM
566
567 g_free(memdb);
568}
569
570int memdb_mkdir(
571 memdb_t *memdb,
572 const char *path,
573 guint32 writer,
574 guint32 mtime)
575{
576 g_return_val_if_fail(memdb != NULL, -EINVAL);
577 g_return_val_if_fail(path != NULL, -EINVAL);
578
579 int ret = -EACCES;
580
581 char *dirname = NULL;
582 char *base = NULL;
583
89fde9ac 584 g_mutex_lock (&memdb->mutex);
fe000966
DM
585
586 if (memdb->errors) {
587 ret = -EIO;
588 goto ret;
589 }
590
591 split_path(path, &dirname, &base);
592
593 memdb_tree_entry_t *parent, *unused;
594
595 if (!(parent = memdb_lookup_path(memdb, dirname, &unused))) {
596 ret = -ENOENT;
597 goto ret;
598 }
599
600 if (parent->type != DT_DIR) {
601 ret = -ENOTDIR;
602 goto ret;
603 }
604
605 /* do not allow '.' and '..' */
606 if ((base[0] == 0) ||
607 (base[0] == '.' && base[1] == 0) ||
608 (base[0] == '.' && base[1] == '.' && base[2] == 0)) {
609 ret = -EACCES;
610 goto ret;
611 }
612
613 memdb_tree_entry_t *te;
614 if ((te = memdb_lookup_dir_entry(memdb, base, parent))) {
615 ret = -EEXIST;
616 goto ret;
617 }
618
619 memdb->root->version++;
620 memdb->root->mtime = mtime;
621 memdb->root->writer = writer;
622
623 te = memdb_tree_entry_new(base);
624 te->parent = parent->inode;
625 te->data.entries = g_hash_table_new(g_str_hash, g_str_equal);
626 te->inode = te->version = memdb->root->version;
627 te->writer = writer;
628 te->type = DT_DIR;
629 te->mtime = mtime;
630
631 g_hash_table_replace(parent->data.entries, te->name, te);
632 g_hash_table_replace(memdb->index, &te->inode, te);
633
e5a5a3ea 634 cfs_debug("memdb_mkdir %s %s %016" PRIX64, dirname, base, memdb->root->version);
fe000966
DM
635
636 if (bdb_backend_write(memdb->bdb, te->inode, te->parent, te->version,
637 te->writer, te->mtime, 0, DT_DIR, te->name, NULL, 0)) {
638 memdb->errors = 1;
639 ret = -EIO;
640 goto ret;
641 }
642
643 if (strcmp(dirname, "priv/lock") == 0) {
644 g_hash_table_remove(memdb->locks, path);
645 guchar csum[32];
646 if (memdb_tree_entry_csum(te, csum)) {
647 memdb_lock_expired(memdb, path, csum); // insert a new entry
648 }
649 }
650
651 ret = 0;
652
653 ret:
89fde9ac 654 g_mutex_unlock (&memdb->mutex);
fe000966
DM
655
656 if (dirname) g_free (dirname);
657 if (base) g_free (base);
658
659 return ret;
660}
661
662int
663memdb_read(
664 memdb_t *memdb,
665 const char *path,
666 gpointer *data_ret)
667{
668 g_return_val_if_fail(memdb != NULL, -EINVAL);
669 g_return_val_if_fail(path != NULL, -EINVAL);
670 g_return_val_if_fail(data_ret != NULL, -EINVAL);
671
672 memdb_tree_entry_t *te, *parent;
673
89fde9ac 674 g_mutex_lock (&memdb->mutex);
fe000966
DM
675
676 if ((te = memdb_lookup_path(memdb, path, &parent))) {
677 if (te->type == DT_REG) {
678 *data_ret = g_memdup(te->data.value, te->size);
679 guint32 size = te->size;
89fde9ac 680 g_mutex_unlock (&memdb->mutex);
fe000966
DM
681 return size;
682 }
683 }
684
89fde9ac 685 g_mutex_unlock (&memdb->mutex);
fe000966
DM
686
687 return -ENOENT;
688}
689
690static int
691memdb_pwrite(
692 memdb_t *memdb,
693 const char *path,
694 guint32 writer,
695 guint32 mtime,
696 gconstpointer data,
697 size_t count,
698 off_t offset,
699 gboolean truncate,
700 gboolean create)
701{
702 g_return_val_if_fail(memdb != NULL, -EINVAL);
703 g_return_val_if_fail(path != NULL, -EINVAL);
704 g_return_val_if_fail(count == 0 || data != NULL, -EINVAL);
705
706 int ret = -EACCES;
707
708 char *dirname = NULL;
709 char *base = NULL;
710 char *nodename = NULL;
711
89fde9ac 712 g_mutex_lock (&memdb->mutex);
fe000966
DM
713
714 if (memdb->errors) {
715 ret = -EIO;
716 goto ret;
717 }
718
719 if ((offset + count) > MEMDB_MAX_FILE_SIZE) {
720 ret = -EFBIG;
721 goto ret;
722 }
723
724 split_path(path, &dirname, &base);
725
726 memdb_tree_entry_t *parent, *unused;
727 if (!(parent = memdb_lookup_path(memdb, dirname, &unused))) {
728 ret = -ENOENT;
729 goto ret;
730 }
731 if (parent->type != DT_DIR) {
732 ret = -ENOTDIR;
733 goto ret;
734 }
735
736 /* do not allow '.' and '..' */
737 if ((base[0] == 0) ||
738 (base[0] == '.' && base[1] == 0) ||
739 (base[0] == '.' && base[1] == '.' && base[2] == 0)) {
740 ret = -EACCES;
741 goto ret;
742 }
743
744 guint32 vmid = 0;
745 int vmtype = 0;
746
747 if ((nodename = path_contain_vm_config(path, &vmtype, &vmid))) {
748 if (vmlist_different_vm_exists(vmtype, vmid, nodename)) {
749 ret = -EEXIST;
750 goto ret;
751 }
752 }
753
754 gpointer olddata = NULL;
755
756 memdb_tree_entry_t *te, *old;
757 if ((old = te = memdb_lookup_dir_entry(memdb, base, parent))) {
758 if (te->type != DT_REG) {
759 ret = -ENOTDIR;
760 goto ret;
761 }
762
763 if (create) {
764 ret = -EEXIST;
765 goto ret;
766 }
767
768 memdb->root->version++;
769 memdb->root->mtime = mtime;
770 memdb->root->writer = writer;
771
772 olddata = te->data.value;
773 } else {
774
775 if (!create) {
776 ret = -ENOENT;
777 goto ret;
778 }
779
780 memdb->root->version++;
781 memdb->root->mtime = mtime;
782 memdb->root->writer = writer;
783
784 te = memdb_tree_entry_new(base);
785 te->parent = parent->inode;
786 te->type = DT_REG;
787 te->inode = memdb->root->version;
788 }
789
790 te->version = memdb->root->version;
791 te->writer = writer;
792 te->mtime = mtime;
793
794 size_t newsize = offset + count;
795
796 gpointer newdata = NULL;
797
798 if (olddata) {
799
800 if (newsize > te->size) {
801 newdata = g_malloc0(newsize);
802 memcpy(newdata, olddata, te->size);
803
804 } else {
805
806 if (!truncate) {
807 newsize = te->size;
808 }
809 newdata = g_malloc0(newsize);
810 memcpy(newdata, olddata, newsize);
811 }
812
813 if (count && data)
814 memcpy(newdata + offset, data, count);
815
816 } else {
817
818 if (count && data) {
819 newdata = g_malloc0(newsize);
820 memcpy(newdata + offset, data, count);
821 }
822 }
823
824 te->size = newsize;
825 te->data.value = newdata;
826
827 g_free(olddata);
828
829 if (!old) {
830 g_hash_table_replace(parent->data.entries, te->name, te);
831 g_hash_table_replace(memdb->index, &te->inode, te);
832 }
833
834 record_memdb_change(path);
835
e5a5a3ea 836 cfs_debug("memdb_pwrite %s %s %016" PRIX64 " %016" PRIX64, dirname, te->name, te->inode, te->version);
fe000966
DM
837
838 if (bdb_backend_write(memdb->bdb, te->inode, te->parent, te->version,
839 te->writer, te->mtime, te->size, te->type, te->name,
840 te->data.value, 0)) {
841 memdb->errors = 1;
842 ret = -EIO;
843 goto ret;
844 }
845
846 if (nodename)
847 vmlist_register_vm(vmtype, vmid, nodename);
848
849 ret = count;
850
851 ret:
89fde9ac 852 g_mutex_unlock (&memdb->mutex);
fe000966
DM
853
854 if (nodename) g_free (nodename);
855 if (dirname) g_free (dirname);
856 if (base) g_free (base);
857
858 return ret;
859}
860
861int
862memdb_mtime(
863 memdb_t *memdb,
864 const char *path,
865 guint32 writer,
866 guint32 mtime)
867{
868 g_return_val_if_fail(memdb != NULL, -EINVAL);
869 g_return_val_if_fail(path != NULL, -EINVAL);
870
871 int ret = -EACCES;
872
873 char *dirname = NULL;
874 char *base = NULL;
875
89fde9ac 876 g_mutex_lock (&memdb->mutex);
fe000966
DM
877
878 if (memdb->errors) {
879 ret = -EIO;
880 goto ret;
881 }
882
883 split_path(path, &dirname, &base);
884
885 memdb_tree_entry_t *parent, *unused;
886 if (!(parent = memdb_lookup_path(memdb, dirname, &unused))) {
887 ret = -ENOENT;
888 goto ret;
889 }
890 if (parent->type != DT_DIR) {
891 ret = -ENOTDIR;
892 goto ret;
893 }
894
895 /* do not allow '.' and '..' */
896 if ((base[0] == 0) ||
897 (base[0] == '.' && base[1] == 0) ||
898 (base[0] == '.' && base[1] == '.' && base[2] == 0)) {
899 ret = -EACCES;
900 goto ret;
901 }
902
903 memdb_tree_entry_t *te;
904 if (!(te = memdb_lookup_dir_entry(memdb, base, parent))) {
905 ret = -ENOENT;
906 goto ret;
907 }
908
894bce1a
DM
909 int is_lock = (strcmp(dirname, "priv/lock") == 0) && (te->type == DT_DIR);
910
fe000966 911 /* NOTE: we use utime(0,0) to trigger 'unlock', so we do not
7f66b436 912 * allow to change mtime for locks (only if mtime is newer).
fe000966
DM
913 * See README for details about locks.
914 */
894bce1a
DM
915 if (is_lock) {
916 if (mtime < te->mtime) {
fe000966
DM
917 cfs_debug("dir is locked");
918 ret = -EACCES;
919 goto ret;
894bce1a
DM
920 } else {
921 /* only allow lock updates if the writer is the same */
922 if (te->writer != writer) {
923 ret = -EACCES;
924 goto ret;
925 }
926 }
fe000966
DM
927 }
928
fe000966
DM
929 memdb->root->version++;
930 memdb->root->mtime = mtime;
931 memdb->root->writer = writer;
932
933 te->version = memdb->root->version;
934 te->writer = writer;
935 te->mtime = mtime;
936
937 record_memdb_change(path);
938
e5a5a3ea 939 cfs_debug("memdb_mtime %s %s %016" PRIX64 " %016" PRIX64, dirname, te->name, te->inode, te->version);
fe000966
DM
940
941 if (bdb_backend_write(memdb->bdb, te->inode, te->parent, te->version,
942 te->writer, te->mtime, te->size, te->type, te->name,
943 te->data.value, 0)) {
944 memdb->errors = 1;
945 ret = -EIO;
946 goto ret;
947 }
948
894bce1a
DM
949 if (is_lock) {
950 cfs_debug("update cfs lock");
951 g_hash_table_remove(memdb->locks, path);
952 guchar csum[32];
953 if (memdb_tree_entry_csum(te, csum)) {
954 memdb_lock_expired(memdb, path, csum); // insert a new entry
955 }
956 }
957
fe000966
DM
958 ret = 0;
959
960 ret:
89fde9ac 961 g_mutex_unlock (&memdb->mutex);
fe000966
DM
962
963 if (dirname) g_free (dirname);
964 if (base) g_free (base);
965
966 return ret;
967}
968
969int
970memdb_create(
971 memdb_t *memdb,
972 const char *path,
973 guint32 writer,
974 guint32 mtime)
975{
976 return memdb_pwrite(memdb, path, writer, mtime, NULL, 0, 0, FALSE, TRUE);
977}
978
979int
980memdb_write(
981 memdb_t *memdb,
982 const char *path,
983 guint32 writer,
984 guint32 mtime,
985 gconstpointer data,
986 size_t count,
987 off_t offset,
988 gboolean truncate)
989{
990 return memdb_pwrite(memdb, path, writer, mtime, data, count, offset, truncate, FALSE);
991}
992
993memdb_tree_entry_t *
994memdb_getattr(
995 memdb_t *memdb,
996 const char *path)
997{
998 memdb_tree_entry_t *te, *parent;
999
89fde9ac 1000 g_mutex_lock (&memdb->mutex);
fe000966
DM
1001
1002 if ((te = memdb_lookup_path(memdb, path, &parent))) {
1003
1004 memdb_tree_entry_t *cpy = memdb_tree_entry_copy(te, 0);
1005
89fde9ac 1006 g_mutex_unlock (&memdb->mutex);
fe000966
DM
1007
1008 return cpy;
1009 }
1010
89fde9ac 1011 g_mutex_unlock (&memdb->mutex);
fe000966
DM
1012
1013 return NULL;
1014}
1015
1016GList *
1017memdb_readdir(
1018 memdb_t *memdb,
1019 const char *path)
1020{
1021 g_return_val_if_fail(memdb != NULL, NULL);
1022 g_return_val_if_fail(path != NULL, NULL);
fe000966
DM
1023
1024 memdb_tree_entry_t *te, *parent;
1025
1026 GList *list = NULL;
1027
89fde9ac 1028 g_mutex_lock (&memdb->mutex);
fe000966
DM
1029
1030 if (!(te = memdb_lookup_path(memdb, path, &parent)))
1031 goto ret;
1032
1033 if (te->type != DT_DIR)
1034 goto ret;
1035
1036 GHashTable *ht = te->data.entries;
1037
1038 GHashTableIter iter;
1039 gpointer key, value;
1040
1041 g_hash_table_iter_init (&iter, ht);
1042
1043 while (g_hash_table_iter_next (&iter, &key, &value)) {
1044
1045 te = (memdb_tree_entry_t *)value;
1046
1047 memdb_tree_entry_t *cpy = memdb_tree_entry_copy(te, 0);
1048
1049 list = g_list_append(list, cpy);
1050 }
1051
1052 ret:
89fde9ac 1053 g_mutex_unlock (&memdb->mutex);
fe000966
DM
1054
1055 return list;
1056}
1057
1058void
1059memdb_dirlist_free(GList *dirlist)
1060{
1061 GList *l = dirlist;
1062
1063 while (l) {
1064 if (l->data)
1065 g_free (l->data);
1066
1067 l = g_list_next(l);
1068 }
1069
1070 if (dirlist)
1071 g_list_free(dirlist);
1072}
1073
1074static int
1075unlink_tree_entry(
1076 memdb_t *memdb,
1077 memdb_tree_entry_t *parent,
1078 memdb_tree_entry_t *te)
1079{
1080 g_return_val_if_fail(parent != NULL, -EACCES);
1081 g_return_val_if_fail(parent->inode == te->parent, -EACCES);
1082
1083 if (te->type == DT_DIR)
1084 if (g_hash_table_size(te->data.entries))
1085 return -ENOTEMPTY;
1086
1087 if (!g_hash_table_steal(parent->data.entries, te->name)) {
1088 cfs_critical("internal error - can't delete entry");
1089 memdb->errors = 1;
1090 return -EIO;
1091 }
1092
1093 if (!g_hash_table_steal(memdb->index, &te->inode)) {
1094 cfs_critical("internal error - can't delete entry");
1095 memdb->errors = 1;
1096 return -EIO;
1097 }
1098
1099 return 0;
1100}
1101
1102int
1103memdb_rename(
1104 memdb_t *memdb,
1105 const char *from,
1106 const char *to,
1107 guint32 writer,
1108 guint32 mtime)
1109{
1110 int ret = -EACCES;
1111
1112 char *nodename = NULL;
1113 char *dirname = NULL;
1114 char *base = NULL;
1115
c3d1300b
DM
1116 guint32 vmid = 0;
1117 guint32 from_vmid = 0;
1118 int vmtype = 0;
1119 int from_vmtype = 0;
1120 char *from_node = NULL;
1121
89fde9ac 1122 g_mutex_lock (&memdb->mutex);
fe000966
DM
1123
1124 if (memdb->errors) {
1125 ret = -EIO;
1126 goto ret;
1127 }
1128
1129 memdb_tree_entry_t *from_te, *from_parent;
1130 memdb_tree_entry_t *to_te, *to_parent;
1131 memdb_tree_entry_t *target_te, *target_parent;
1132
1133 guint64 delete_inode = 0;
1134
1135 if (!(from_te = memdb_lookup_path(memdb, from, &from_parent))) {
1136 ret = -ENOENT;
1137 goto ret;
1138 }
1139
1140 if (!from_parent) { /* can't rename root */
1141 ret = -EACCES;
1142 goto ret;
1143 }
1144
c3d1300b 1145 from_node = path_contain_vm_config(from, &from_vmtype, &from_vmid);
fe000966 1146
6e73d5c2 1147 if (from_te->type == DT_REG && (nodename = path_contain_vm_config(to, &vmtype, &vmid))) {
e6be001a 1148 if (vmlist_different_vm_exists(vmtype, vmid, nodename)) {
8c6a4084 1149 if (!(from_node && vmid == from_vmid)) {
b382cba9
DM
1150 ret = -EEXIST;
1151 goto ret;
6e73d5c2 1152 }
fe000966
DM
1153 }
1154 }
1155
1156 /* we do not allow rename for locks */
1157 if (from_te->type == DT_DIR && path_is_lockdir(from)) {
1158 ret = -EACCES;
1159 goto ret;
1160 }
1161
1162 if ((to_te = memdb_lookup_path(memdb, to, &to_parent))) {
1163
1164 if ((ret = unlink_tree_entry(memdb, to_parent, to_te)) != 0)
1165 goto ret;
1166
1167 base = strdup(to_te->name);
1168
1169 delete_inode = to_te->inode;
1170
1171 target_te = to_parent;
1172
1173 memdb_tree_entry_free(to_te);
1174
1175 } else {
1176
1177 split_path(to, &dirname, &base);
1178
1179 if (!(target_te = memdb_lookup_path(memdb, dirname, &target_parent))) {
1180 ret = -ENOENT;
1181 goto ret;
1182 }
1183
1184 if (target_te->type != DT_DIR) {
1185 ret = -ENOTDIR;
1186 goto ret;
1187 }
1188 }
1189
1190 record_memdb_change(from);
1191 record_memdb_change(to);
1192
1193 /* NOTE: unlink_tree_entry() make sure that we can only
1194 rename emtpy directories */
1195
1196 if ((ret = unlink_tree_entry(memdb, from_parent, from_te)) != 0)
1197 goto ret;
1198
1199 memdb->root->version++;
1200 memdb->root->mtime = mtime;
1201 memdb->root->writer = writer;
1202
1203 memdb_tree_entry_t *new = memdb_tree_entry_new(base);
1204 new->parent = target_te->inode;
1205 new->inode = from_te->inode;
1206 new->version = memdb->root->version;
1207 new->writer = writer;
1208 new->mtime = mtime;
1209 new->size = from_te->size;
1210 new->type = from_te->type;
1211 new->data = from_te->data;
1212
1213 g_free(from_te);
1214
1215 g_hash_table_replace(target_te->data.entries, new->name, new);
1216 g_hash_table_replace(memdb->index, &new->inode, new);
1217
1218 if (bdb_backend_write(memdb->bdb, new->inode, new->parent,
1219 new->version, new->writer, new->mtime,
1220 new->size, new->type, new->name,
1221 new->data.value, delete_inode)) {
1222 memdb->errors = 1;
1223 ret = -EIO;
1224 goto ret;
1225 }
1226
1227 if (new->type == DT_REG) {
1228
1229 if (from_vmid)
1230 vmlist_delete_vm(from_vmid);
1231
1232 if (nodename)
1233 vmlist_register_vm(vmtype, vmid, nodename);
1234
1235 } else if (new->type == DT_DIR) {
1236 /* directories are alwayse empty (see unlink_tree_entry) */
1237 }
1238
1239 ret = 0;
1240
1241 ret:
89fde9ac 1242 g_mutex_unlock (&memdb->mutex);
fe000966 1243
b382cba9 1244 if (from_node) g_free(from_node);
fe000966
DM
1245 if (nodename) g_free (nodename);
1246 if (dirname) g_free (dirname);
1247 if (base) g_free (base);
1248
1249 return ret;
1250}
1251
1252int
1253memdb_delete(
1254 memdb_t *memdb,
1255 const char *path,
1256 guint32 writer,
1257 guint32 mtime)
1258{
1259 memdb_tree_entry_t *te, *parent;
1260
89fde9ac 1261 g_mutex_lock (&memdb->mutex);
fe000966
DM
1262
1263 int ret = -EACCES;
1264
1265 if (memdb->errors) {
1266 ret = -EIO;
1267 goto ret;
1268 }
1269
1270 if (!(te = memdb_lookup_path(memdb, path, &parent))) {
1271 ret = -ENOENT;
1272 goto ret;
1273 }
1274
1275 if (!parent) { /* cant remove root */
1276 ret = -EACCES;
1277 goto ret;
1278 }
1279
1280 if (te->type == DT_DIR) {
1281 if (g_hash_table_size(te->data.entries)) {
1282 ret = -ENOTEMPTY;
1283 goto ret;
1284 }
1285
1286 g_hash_table_remove(memdb->locks, path);
1287 }
1288
1289 record_memdb_change(path);
1290
1291 if ((ret = unlink_tree_entry(memdb, parent, te)) != 0)
1292 goto ret;
1293
1294 memdb->root->version++;
1295 memdb->root->mtime = mtime;
1296 memdb->root->writer = writer;
1297
1298 if (bdb_backend_write(memdb->bdb, 0, 0, memdb->root->version, writer, mtime, 0,
1299 DT_REG, NULL, NULL, te->inode)) {
1300 memdb->errors = 1;
1301 memdb_tree_entry_free(te);
1302 ret = -EIO;
1303 goto ret;
1304 }
1305
1306 memdb_tree_entry_free(te);
1307
1308 int vmtype = 0;
1309 guint32 vmid = 0;
1310 char *nodename;
1311 if ((nodename = path_contain_vm_config(path, &vmtype, &vmid))) {
1312 g_free(nodename);
1313 vmlist_delete_vm(vmid);
1314 }
1315
1316 ret = 0;
1317
1318 ret:
89fde9ac 1319 g_mutex_unlock (&memdb->mutex);
fe000966
DM
1320
1321 return ret;
1322}
1323
1324int
1325memdb_statfs(
1326 memdb_t *memdb,
1327 struct statvfs *stbuf)
1328{
1329 g_return_val_if_fail(memdb != NULL, -EINVAL);
1330 g_return_val_if_fail(stbuf != NULL, -EINVAL);
1331
89fde9ac 1332 g_mutex_lock (&memdb->mutex);
fe000966
DM
1333
1334 GHashTableIter iter;
1335 gpointer key, value;
1336
1337 size_t size = 0;
1338 size_t files = 0;
1339
1340 g_hash_table_iter_init (&iter, memdb->index);
1341
1342 while (g_hash_table_iter_next (&iter, &key, &value)) {
1343 memdb_tree_entry_t *te = (memdb_tree_entry_t *)value;
1344 files++;
1345 size += te->size;
1346 }
1347
89fde9ac 1348 g_mutex_unlock (&memdb->mutex);
fe000966
DM
1349
1350 stbuf->f_bsize = MEMDB_BLOCKSIZE;
1351 stbuf->f_blocks = MEMDB_BLOCKS;
1352 stbuf->f_bfree = stbuf->f_bavail = stbuf->f_blocks -
1353 ((size + stbuf->f_bsize - 1)/stbuf->f_bsize);
1354 stbuf->f_files = MEMDB_MAX_INODES;
1355 stbuf->f_ffree = stbuf->f_files - files;
1356
1357 stbuf->f_namemax = 256;
1358
1359 return 0;
1360}
1361
1362void
1363tree_entry_debug(memdb_tree_entry_t *te)
1364{
1365 g_return_if_fail(te != NULL);
1366
1367 // same as tree_entry_print(), but use cfs_debug() instead of g_print()
1368
e5a5a3ea 1369 cfs_debug("%016" PRIX64 " %c %016" PRIX64 " %016" PRIX64 " %08X %08X %08X %s\n",
fe000966
DM
1370 te->inode, te->type == DT_DIR ? 'D' : 'R', te->parent, te->version,
1371 te->writer, te->mtime, te->size, te->name);
1372}
1373
1374void
1375tree_entry_print(memdb_tree_entry_t *te)
1376{
1377 g_return_if_fail(te != NULL);
1378
e5a5a3ea 1379 g_print("%016" PRIX64 " %c %016" PRIX64 " %016" PRIX64 " %08X %08X %08X %s\n",
fe000966
DM
1380 te->inode, te->type == DT_DIR ? 'D' : 'R', te->parent, te->version,
1381 te->writer, te->mtime, te->size, te->name);
1382}
1383
1384void
1385memdb_dump(memdb_t *memdb)
1386{
1387 g_return_if_fail(memdb != NULL);
1388
89fde9ac 1389 g_mutex_lock (&memdb->mutex);
fe000966
DM
1390
1391 GList *list = g_hash_table_get_values(memdb->index);
1392
1393 list = g_list_sort(list, memdb_tree_compare);
1394
1395 g_print("%16s %c %16s %16s %8s %8s %8s %s\n",
1396 "INODE", 'T', "PARENT", "VERSION", "WRITER", "MTIME", "SIZE", "NAME");
1397
1398 GList *l = list;
1399 while (l) {
1400 memdb_tree_entry_t *te = (memdb_tree_entry_t *)l->data;
1401
1402 tree_entry_print(te);
1403
1404 l = g_list_next(l);
1405 }
1406
1407 g_list_free(list);
1408
89fde9ac 1409 g_mutex_unlock (&memdb->mutex);
fe000966
DM
1410}
1411
1412void
1413memdb_dump_index (memdb_index_t *idx)
1414{
1415 g_return_if_fail(idx != NULL);
1416
e5a5a3ea 1417 g_print ("INDEX DUMP %016" PRIX64 "\n", idx->version);
fe000966
DM
1418
1419 int i;
1420 for (i = 0; i < idx->size; i++) {
e5a5a3ea 1421 g_print ("%016" PRIX64 " %016" PRIX64 "%016" PRIX64 "%016" PRIX64 "%016" PRIX64 "\n", idx->entries[i].inode,
fe000966
DM
1422 *((guint64 *)idx->entries[i].digest),
1423 *((guint64 *)(idx->entries[i].digest + 8)),
1424 *((guint64 *)(idx->entries[i].digest + 16)),
1425 *((guint64 *)(idx->entries[i].digest + 24)));
1426 }
1427}
1428
1429memdb_index_t *
1430memdb_index_copy(memdb_index_t *idx)
1431{
1432 g_return_val_if_fail(idx != NULL, NULL);
1433
1434 int bytes = sizeof(memdb_index_t) + idx->size*sizeof(memdb_index_extry_t);
1435 if (idx->bytes != bytes) {
1436 cfs_critical("memdb index contains wrong number of bytes");
1437 return NULL;
1438 }
1439
1440 memdb_index_t *copy = (memdb_index_t *)g_memdup(idx, bytes);
1441
1442 return copy;
1443}
1444
1445gboolean
1446memdb_tree_entry_csum(
1447 memdb_tree_entry_t *te,
1448 guchar csum[32])
1449{
1450 g_return_val_if_fail(te != NULL, FALSE);
1451 g_return_val_if_fail(csum != NULL, FALSE);
1452
1453 GChecksum *sha256 = g_checksum_new(G_CHECKSUM_SHA256);
1454
1455 g_checksum_update(sha256, (unsigned char*)&te->inode, sizeof(te->inode));
1456 g_checksum_update(sha256, (unsigned char*)&te->version, sizeof(te->version));
1457 g_checksum_update(sha256, (unsigned char*)&te->writer, sizeof(te->writer));
1458 g_checksum_update(sha256, (unsigned char*)&te->mtime, sizeof(te->mtime));
1459 g_checksum_update(sha256, (unsigned char*)&te->size, sizeof(te->size));
1460 g_checksum_update(sha256, (unsigned char*)&te->type, sizeof(te->type));
1461 g_checksum_update(sha256, (unsigned char*)&te->parent, sizeof(te->parent));
1462 g_checksum_update(sha256, (unsigned char*)te->name, strlen(te->name));
1463
1464 if (te->type == DT_REG && te->size)
1465 g_checksum_update(sha256, (unsigned char*)te->data.value, te->size);
1466
1467 size_t csum_len = 32;
1468 g_checksum_get_digest(sha256, csum, &csum_len);
1469 g_checksum_free(sha256);
1470
1471 return TRUE;
1472}
1473
1474gboolean
1475memdb_compute_checksum(
1476 GHashTable *index,
1477 memdb_tree_entry_t *root,
1478 guchar *csum,
1479 size_t csum_len)
1480{
1481 g_return_val_if_fail(index != NULL, FALSE);
1482 g_return_val_if_fail(root != NULL, FALSE);
1483
1484 GChecksum *sha256 = g_checksum_new(G_CHECKSUM_SHA256);
1485
1486 GList *list = g_hash_table_get_values(index);
1487
1488 list = g_list_sort(list, memdb_tree_compare);
1489
1490 GList *l = list;
1491 while (l) {
1492 memdb_tree_entry_t *te = (memdb_tree_entry_t *)l->data;
1493
1494 g_checksum_update(sha256, (unsigned char*)&te->inode, sizeof(te->inode));
1495 g_checksum_update(sha256, (unsigned char*)&te->version, sizeof(te->version));
1496 g_checksum_update(sha256, (unsigned char*)&te->writer, sizeof(te->writer));
1497 g_checksum_update(sha256, (unsigned char*)&te->mtime, sizeof(te->mtime));
1498 g_checksum_update(sha256, (unsigned char*)&te->size, sizeof(te->size));
1499 g_checksum_update(sha256, (unsigned char*)&te->type, sizeof(te->type));
1500 g_checksum_update(sha256, (unsigned char*)&te->parent, sizeof(te->parent));
1501 g_checksum_update(sha256, (unsigned char*)te->name, strlen(te->name));
1502
1503 if (te->type == DT_REG && te->size)
1504 g_checksum_update(sha256, (unsigned char*)te->data.value, te->size);
1505
1506 l = g_list_next(l);
1507 }
1508
1509 g_list_free(list);
1510
1511 g_checksum_get_digest(sha256, csum, &csum_len);
1512
1513 cfs_debug("checksum: %s", g_checksum_get_string(sha256));
1514
1515 g_checksum_free(sha256);
1516
1517 return TRUE;
1518}
1519
1520memdb_index_t *
1521memdb_encode_index(
1522 GHashTable *index,
1523 memdb_tree_entry_t *root)
1524{
1525 g_return_val_if_fail(index != NULL, NULL);
1526 g_return_val_if_fail(root != NULL, NULL);
1527
1528 memdb_index_t *idx = NULL;
1529
1530 int count = g_hash_table_size(index);
1531 if (!count) {
1532 cfs_critical("memdb index has no entires");
1533 return NULL;
1534 }
1535
1536 int bytes = sizeof(memdb_index_t) + count*sizeof(memdb_index_extry_t);
1537 idx = g_malloc0(bytes);
1538
1539 idx->size = count;
1540 idx->bytes = bytes;
1541 idx->version = root->version;
1542 idx->mtime = root->mtime;
1543 idx->writer = root->writer;
1544
1545 GChecksum *sha256 = g_checksum_new(G_CHECKSUM_SHA256);
1546
1547 GList *list = g_hash_table_get_values(index);
1548
1549 list = g_list_sort(list, memdb_tree_compare);
1550
1551 int ind = 0;
1552 GList *l = list;
1553 while (l) {
1554 memdb_tree_entry_t *te = (memdb_tree_entry_t *)l->data;
1555
1556 if (te->inode > idx->last_inode)
1557 idx->last_inode = te->inode;
1558
1559 idx->entries[ind].inode = te->inode;
1560
1561 g_checksum_reset (sha256);
1562
1563 g_checksum_update(sha256, (unsigned char*)&te->version, sizeof(te->version));
1564 g_checksum_update(sha256, (unsigned char*)&te->writer, sizeof(te->writer));
1565 g_checksum_update(sha256, (unsigned char*)&te->mtime, sizeof(te->mtime));
1566 g_checksum_update(sha256, (unsigned char*)&te->size, sizeof(te->size));
1567 g_checksum_update(sha256, (unsigned char*)&te->type, sizeof(te->type));
1568 g_checksum_update(sha256, (unsigned char*)&te->parent, sizeof(te->parent));
1569 g_checksum_update(sha256, (unsigned char*)te->name, strlen(te->name));
1570
1571 if (te->type == DT_REG && te->size)
1572 g_checksum_update(sha256, (unsigned char*)te->data.value, te->size);
1573
1574 gsize len = 32;
1575 g_checksum_get_digest(sha256, (guint8 *)idx->entries[ind].digest, &len);
1576
1577 ind++;
1578
1579 l = g_list_next(l);
1580 }
1581
1582 g_list_free(list);
1583
1584 g_checksum_free(sha256);
1585
1586 return idx;
1587}