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