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