1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 #include "common.h"
4
5 #include "log.h"
6
7 #include <jansson.h>
8 #include <openssl/sha.h>
9
10 #include "utils.h"
11 #include "db.h"
12 #include "searpc-utils.h"
13
14 #include "seafile-session.h"
15 #include "commit-mgr.h"
16 #include "seaf-utils.h"
17
18 #define MAX_TIME_SKEW 259200 /* 3 days */
19
20 struct _SeafCommitManagerPriv {
21 int dummy;
22 };
23
24 static SeafCommit *
25 load_commit (SeafCommitManager *mgr,
26 const char *repo_id, int version,
27 const char *commit_id);
28 static int
29 save_commit (SeafCommitManager *manager,
30 const char *repo_id, int version,
31 SeafCommit *commit);
32 static void
33 delete_commit (SeafCommitManager *mgr,
34 const char *repo_id, int version,
35 const char *id);
36 static json_t *
37 commit_to_json_object (SeafCommit *commit);
38 static SeafCommit *
39 commit_from_json_object (const char *id, json_t *object);
40
compute_commit_id(SeafCommit * commit)41 static void compute_commit_id (SeafCommit* commit)
42 {
43 SHA_CTX ctx;
44 uint8_t sha1[20];
45 gint64 ctime_n;
46
47 SHA1_Init (&ctx);
48 SHA1_Update (&ctx, commit->root_id, 41);
49 SHA1_Update (&ctx, commit->creator_id, 41);
50 if (commit->creator_name)
51 SHA1_Update (&ctx, commit->creator_name, strlen(commit->creator_name)+1);
52 SHA1_Update (&ctx, commit->desc, strlen(commit->desc)+1);
53
54 /* convert to network byte order */
55 ctime_n = hton64 (commit->ctime);
56 SHA1_Update (&ctx, &ctime_n, sizeof(ctime_n));
57 SHA1_Final (sha1, &ctx);
58
59 rawdata_to_hex (sha1, commit->commit_id, 20);
60 }
61
62 SeafCommit*
seaf_commit_new(const char * commit_id,const char * repo_id,const char * root_id,const char * creator_name,const char * creator_id,const char * desc,guint64 ctime)63 seaf_commit_new (const char *commit_id,
64 const char *repo_id,
65 const char *root_id,
66 const char *creator_name,
67 const char *creator_id,
68 const char *desc,
69 guint64 ctime)
70 {
71 SeafCommit *commit;
72
73 g_return_val_if_fail (repo_id != NULL, NULL);
74 g_return_val_if_fail (root_id != NULL && creator_id != NULL, NULL);
75
76 commit = g_new0 (SeafCommit, 1);
77
78 memcpy (commit->repo_id, repo_id, 36);
79 commit->repo_id[36] = '\0';
80
81 memcpy (commit->root_id, root_id, 40);
82 commit->root_id[40] = '\0';
83
84 commit->creator_name = g_strdup (creator_name);
85
86 memcpy (commit->creator_id, creator_id, 40);
87 commit->creator_id[40] = '\0';
88
89 commit->desc = g_strdup (desc);
90
91 if (ctime == 0) {
92 /* TODO: use more precise timer */
93 commit->ctime = (gint64)time(NULL);
94 } else
95 commit->ctime = ctime;
96
97 if (commit_id == NULL)
98 compute_commit_id (commit);
99 else {
100 memcpy (commit->commit_id, commit_id, 40);
101 commit->commit_id[40] = '\0';
102 }
103
104 commit->ref = 1;
105 return commit;
106 }
107
108 char *
seaf_commit_to_data(SeafCommit * commit,gsize * len)109 seaf_commit_to_data (SeafCommit *commit, gsize *len)
110 {
111 json_t *object;
112 char *json_data;
113 char *ret;
114
115 object = commit_to_json_object (commit);
116
117 json_data = json_dumps (object, 0);
118 *len = strlen (json_data);
119 json_decref (object);
120
121 ret = g_strdup (json_data);
122 free (json_data);
123 return ret;
124 }
125
126 SeafCommit *
seaf_commit_from_data(const char * id,char * data,gsize len)127 seaf_commit_from_data (const char *id, char *data, gsize len)
128 {
129 json_t *object;
130 SeafCommit *commit;
131 json_error_t jerror;
132
133 object = json_loadb (data, len, 0, &jerror);
134 if (!object) {
135 /* Perhaps the commit object contains invalid UTF-8 character. */
136 if (data[len-1] == 0)
137 clean_utf8_data (data, len - 1);
138 else
139 clean_utf8_data (data, len);
140
141 object = json_loadb (data, len, 0, &jerror);
142 if (!object) {
143 if (jerror.text)
144 seaf_warning ("Failed to load commit json: %s.\n", jerror.text);
145 else
146 seaf_warning ("Failed to load commit json.\n");
147 return NULL;
148 }
149 }
150
151 commit = commit_from_json_object (id, object);
152
153 json_decref (object);
154
155 return commit;
156 }
157
158 static void
seaf_commit_free(SeafCommit * commit)159 seaf_commit_free (SeafCommit *commit)
160 {
161 g_free (commit->desc);
162 g_free (commit->creator_name);
163 if (commit->parent_id) g_free (commit->parent_id);
164 if (commit->second_parent_id) g_free (commit->second_parent_id);
165 if (commit->repo_name) g_free (commit->repo_name);
166 if (commit->repo_desc) g_free (commit->repo_desc);
167 if (commit->device_name) g_free (commit->device_name);
168 g_free (commit->client_version);
169 g_free (commit->magic);
170 g_free (commit->random_key);
171 g_free (commit);
172 }
173
174 void
seaf_commit_ref(SeafCommit * commit)175 seaf_commit_ref (SeafCommit *commit)
176 {
177 commit->ref++;
178 }
179
180 void
seaf_commit_unref(SeafCommit * commit)181 seaf_commit_unref (SeafCommit *commit)
182 {
183 if (!commit)
184 return;
185
186 if (--commit->ref <= 0)
187 seaf_commit_free (commit);
188 }
189
190 SeafCommitManager*
seaf_commit_manager_new(SeafileSession * seaf)191 seaf_commit_manager_new (SeafileSession *seaf)
192 {
193 SeafCommitManager *mgr = g_new0 (SeafCommitManager, 1);
194
195 mgr->priv = g_new0 (SeafCommitManagerPriv, 1);
196 mgr->seaf = seaf;
197 mgr->obj_store = seaf_obj_store_new (mgr->seaf, "commits");
198
199 return mgr;
200 }
201
202 int
seaf_commit_manager_init(SeafCommitManager * mgr)203 seaf_commit_manager_init (SeafCommitManager *mgr)
204 {
205 if (seaf_obj_store_init (mgr->obj_store) < 0) {
206 seaf_warning ("[commit mgr] Failed to init commit object store.\n");
207 return -1;
208 }
209 return 0;
210 }
211
212 #if 0
213 inline static void
214 add_commit_to_cache (SeafCommitManager *mgr, SeafCommit *commit)
215 {
216 g_hash_table_insert (mgr->priv->commit_cache,
217 g_strdup(commit->commit_id),
218 commit);
219 seaf_commit_ref (commit);
220 }
221
222 inline static void
223 remove_commit_from_cache (SeafCommitManager *mgr, SeafCommit *commit)
224 {
225 g_hash_table_remove (mgr->priv->commit_cache, commit->commit_id);
226 seaf_commit_unref (commit);
227 }
228 #endif
229
230 int
seaf_commit_manager_add_commit(SeafCommitManager * mgr,SeafCommit * commit)231 seaf_commit_manager_add_commit (SeafCommitManager *mgr,
232 SeafCommit *commit)
233 {
234 int ret;
235
236 /* add_commit_to_cache (mgr, commit); */
237 if ((ret = save_commit (mgr, commit->repo_id, commit->version, commit)) < 0)
238 return -1;
239
240 return 0;
241 }
242
243 void
seaf_commit_manager_del_commit(SeafCommitManager * mgr,const char * repo_id,int version,const char * id)244 seaf_commit_manager_del_commit (SeafCommitManager *mgr,
245 const char *repo_id,
246 int version,
247 const char *id)
248 {
249 g_return_if_fail (id != NULL);
250
251 #if 0
252 commit = g_hash_table_lookup(mgr->priv->commit_cache, id);
253 if (!commit)
254 goto delete;
255
256 /*
257 * Catch ref count bug here. We have bug in commit ref, the
258 * following assert can't pass. TODO: fix the commit ref bug
259 */
260 /* g_assert (commit->ref <= 1); */
261 remove_commit_from_cache (mgr, commit);
262
263 delete:
264 #endif
265
266 delete_commit (mgr, repo_id, version, id);
267 }
268
269 SeafCommit*
seaf_commit_manager_get_commit(SeafCommitManager * mgr,const char * repo_id,int version,const char * id)270 seaf_commit_manager_get_commit (SeafCommitManager *mgr,
271 const char *repo_id,
272 int version,
273 const char *id)
274 {
275 SeafCommit *commit;
276
277 #if 0
278 commit = g_hash_table_lookup (mgr->priv->commit_cache, id);
279 if (commit != NULL) {
280 seaf_commit_ref (commit);
281 return commit;
282 }
283 #endif
284
285 commit = load_commit (mgr, repo_id, version, id);
286 if (!commit)
287 return NULL;
288
289 /* add_commit_to_cache (mgr, commit); */
290
291 return commit;
292 }
293
294 SeafCommit *
seaf_commit_manager_get_commit_compatible(SeafCommitManager * mgr,const char * repo_id,const char * id)295 seaf_commit_manager_get_commit_compatible (SeafCommitManager *mgr,
296 const char *repo_id,
297 const char *id)
298 {
299 SeafCommit *commit = NULL;
300
301 /* First try version 1 layout. */
302 commit = seaf_commit_manager_get_commit (mgr, repo_id, 1, id);
303 if (commit)
304 return commit;
305
306 #if defined MIGRATION || defined SEAFILE_CLIENT
307 /* For compatibility with version 0. */
308 commit = seaf_commit_manager_get_commit (mgr, repo_id, 0, id);
309 #endif
310 return commit;
311 }
312
313 static gint
compare_commit_by_time(gconstpointer a,gconstpointer b,gpointer unused)314 compare_commit_by_time (gconstpointer a, gconstpointer b, gpointer unused)
315 {
316 const SeafCommit *commit_a = a;
317 const SeafCommit *commit_b = b;
318
319 /* Latest commit comes first in the list. */
320 return (commit_b->ctime - commit_a->ctime);
321 }
322
323 inline static int
insert_parent_commit(GList ** list,GHashTable * hash,const char * repo_id,int version,const char * parent_id,gboolean allow_truncate)324 insert_parent_commit (GList **list, GHashTable *hash,
325 const char *repo_id, int version,
326 const char *parent_id, gboolean allow_truncate)
327 {
328 SeafCommit *p;
329 char *key;
330
331 if (g_hash_table_lookup (hash, parent_id) != NULL)
332 return 0;
333
334 p = seaf_commit_manager_get_commit (seaf->commit_mgr,
335 repo_id, version,
336 parent_id);
337 if (!p) {
338 if (allow_truncate)
339 return 0;
340 seaf_warning ("Failed to find commit %s\n", parent_id);
341 return -1;
342 }
343
344 *list = g_list_insert_sorted_with_data (*list, p,
345 compare_commit_by_time,
346 NULL);
347
348 key = g_strdup (parent_id);
349 g_hash_table_replace (hash, key, key);
350
351 return 0;
352 }
353
354 gboolean
seaf_commit_manager_traverse_commit_tree_with_limit(SeafCommitManager * mgr,const char * repo_id,int version,const char * head,CommitTraverseFunc func,int limit,void * data,char ** next_start_commit,gboolean skip_errors)355 seaf_commit_manager_traverse_commit_tree_with_limit (SeafCommitManager *mgr,
356 const char *repo_id,
357 int version,
358 const char *head,
359 CommitTraverseFunc func,
360 int limit,
361 void *data,
362 char **next_start_commit,
363 gboolean skip_errors)
364 {
365 SeafCommit *commit;
366 GList *list = NULL;
367 GHashTable *commit_hash;
368 gboolean ret = TRUE;
369
370 /* A hash table for recording id of traversed commits. */
371 commit_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
372
373 commit = seaf_commit_manager_get_commit (mgr, repo_id, version, head);
374 if (!commit) {
375 seaf_warning ("Failed to find commit %s.\n", head);
376 g_hash_table_destroy (commit_hash);
377 return FALSE;
378 }
379
380 list = g_list_insert_sorted_with_data (list, commit,
381 compare_commit_by_time,
382 NULL);
383
384 char *key = g_strdup (commit->commit_id);
385 g_hash_table_replace (commit_hash, key, key);
386
387 int count = 0;
388 while (list) {
389 gboolean stop = FALSE;
390 commit = list->data;
391 list = g_list_delete_link (list, list);
392
393 if (!func (commit, data, &stop)) {
394 if (!skip_errors) {
395 seaf_commit_unref (commit);
396 ret = FALSE;
397 goto out;
398 }
399 }
400
401 if (stop) {
402 seaf_commit_unref (commit);
403 /* stop traverse down from this commit,
404 * but not stop traversing the tree
405 */
406 continue;
407 }
408
409 if (commit->parent_id) {
410 if (insert_parent_commit (&list, commit_hash, repo_id, version,
411 commit->parent_id, FALSE) < 0) {
412 if (!skip_errors) {
413 seaf_commit_unref (commit);
414 ret = FALSE;
415 goto out;
416 }
417 }
418 }
419 if (commit->second_parent_id) {
420 if (insert_parent_commit (&list, commit_hash, repo_id, version,
421 commit->second_parent_id, FALSE) < 0) {
422 if (!skip_errors) {
423 seaf_commit_unref (commit);
424 ret = FALSE;
425 goto out;
426 }
427 }
428 }
429 seaf_commit_unref (commit);
430
431 /* Stop when limit is reached and don't stop at unmerged branch.
432 * If limit < 0, there is no limit;
433 */
434 if (limit > 0 && ++count >= limit && (!list || !list->next)) {
435 break;
436 }
437 }
438 /*
439 * two scenarios:
440 * 1. list is empty, indicate scan end
441 * 2. list only have one commit, as start for next scan
442 */
443 if (list) {
444 commit = list->data;
445 if (next_start_commit) {
446 *next_start_commit= g_strdup (commit->commit_id);
447 }
448 seaf_commit_unref (commit);
449 list = g_list_delete_link (list, list);
450 }
451
452 out:
453 g_hash_table_destroy (commit_hash);
454 while (list) {
455 commit = list->data;
456 seaf_commit_unref (commit);
457 list = g_list_delete_link (list, list);
458 }
459 return ret;
460 }
461
462 static gboolean
traverse_commit_tree_common(SeafCommitManager * mgr,const char * repo_id,int version,const char * head,CommitTraverseFunc func,void * data,gboolean skip_errors,gboolean allow_truncate)463 traverse_commit_tree_common (SeafCommitManager *mgr,
464 const char *repo_id,
465 int version,
466 const char *head,
467 CommitTraverseFunc func,
468 void *data,
469 gboolean skip_errors,
470 gboolean allow_truncate)
471 {
472 SeafCommit *commit;
473 GList *list = NULL;
474 GHashTable *commit_hash;
475 gboolean ret = TRUE;
476
477 commit = seaf_commit_manager_get_commit (mgr, repo_id, version, head);
478 if (!commit) {
479 seaf_warning ("Failed to find commit %s.\n", head);
480 // For head commit damaged, directly return FALSE
481 // user can repair head by fsck then retraverse the tree
482 return FALSE;
483 }
484
485 /* A hash table for recording id of traversed commits. */
486 commit_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
487
488 list = g_list_insert_sorted_with_data (list, commit,
489 compare_commit_by_time,
490 NULL);
491
492 char *key = g_strdup (commit->commit_id);
493 g_hash_table_replace (commit_hash, key, key);
494
495 while (list) {
496 gboolean stop = FALSE;
497 commit = list->data;
498 list = g_list_delete_link (list, list);
499
500 if (!func (commit, data, &stop)) {
501 seaf_warning("[comit-mgr] CommitTraverseFunc failed\n");
502
503 /* If skip errors, continue to traverse parents. */
504 if (!skip_errors) {
505 seaf_commit_unref (commit);
506 ret = FALSE;
507 goto out;
508 }
509 }
510 if (stop) {
511 seaf_commit_unref (commit);
512 /* stop traverse down from this commit,
513 * but not stop traversing the tree
514 */
515 continue;
516 }
517
518 if (commit->parent_id) {
519 if (insert_parent_commit (&list, commit_hash, repo_id, version,
520 commit->parent_id, allow_truncate) < 0) {
521 seaf_warning("[comit-mgr] insert parent commit failed\n");
522
523 /* If skip errors, try insert second parent. */
524 if (!skip_errors) {
525 seaf_commit_unref (commit);
526 ret = FALSE;
527 goto out;
528 }
529 }
530 }
531 if (commit->second_parent_id) {
532 if (insert_parent_commit (&list, commit_hash, repo_id, version,
533 commit->second_parent_id, allow_truncate) < 0) {
534 seaf_warning("[comit-mgr]insert second parent commit failed\n");
535
536 if (!skip_errors) {
537 seaf_commit_unref (commit);
538 ret = FALSE;
539 goto out;
540 }
541 }
542 }
543 seaf_commit_unref (commit);
544 }
545
546 out:
547 g_hash_table_destroy (commit_hash);
548 while (list) {
549 commit = list->data;
550 seaf_commit_unref (commit);
551 list = g_list_delete_link (list, list);
552 }
553 return ret;
554 }
555
556 gboolean
seaf_commit_manager_traverse_commit_tree(SeafCommitManager * mgr,const char * repo_id,int version,const char * head,CommitTraverseFunc func,void * data,gboolean skip_errors)557 seaf_commit_manager_traverse_commit_tree (SeafCommitManager *mgr,
558 const char *repo_id,
559 int version,
560 const char *head,
561 CommitTraverseFunc func,
562 void *data,
563 gboolean skip_errors)
564 {
565 return traverse_commit_tree_common (mgr, repo_id, version, head,
566 func, data, skip_errors, FALSE);
567 }
568
569 gboolean
seaf_commit_manager_traverse_commit_tree_truncated(SeafCommitManager * mgr,const char * repo_id,int version,const char * head,CommitTraverseFunc func,void * data,gboolean skip_errors)570 seaf_commit_manager_traverse_commit_tree_truncated (SeafCommitManager *mgr,
571 const char *repo_id,
572 int version,
573 const char *head,
574 CommitTraverseFunc func,
575 void *data,
576 gboolean skip_errors)
577 {
578 return traverse_commit_tree_common (mgr, repo_id, version, head,
579 func, data, skip_errors, TRUE);
580 }
581
582 gboolean
seaf_commit_manager_commit_exists(SeafCommitManager * mgr,const char * repo_id,int version,const char * id)583 seaf_commit_manager_commit_exists (SeafCommitManager *mgr,
584 const char *repo_id,
585 int version,
586 const char *id)
587 {
588 #if 0
589 commit = g_hash_table_lookup (mgr->priv->commit_cache, id);
590 if (commit != NULL)
591 return TRUE;
592 #endif
593
594 return seaf_obj_store_obj_exists (mgr->obj_store, repo_id, version, id);
595 }
596
597 static json_t *
commit_to_json_object(SeafCommit * commit)598 commit_to_json_object (SeafCommit *commit)
599 {
600 json_t *object;
601
602 object = json_object ();
603
604 json_object_set_string_member (object, "commit_id", commit->commit_id);
605 json_object_set_string_member (object, "root_id", commit->root_id);
606 json_object_set_string_member (object, "repo_id", commit->repo_id);
607 if (commit->creator_name)
608 json_object_set_string_member (object, "creator_name", commit->creator_name);
609 json_object_set_string_member (object, "creator", commit->creator_id);
610 json_object_set_string_member (object, "description", commit->desc);
611 json_object_set_int_member (object, "ctime", (gint64)commit->ctime);
612 json_object_set_string_or_null_member (object, "parent_id", commit->parent_id);
613 json_object_set_string_or_null_member (object, "second_parent_id",
614 commit->second_parent_id);
615 /*
616 * also save repo's properties to commit file, for easy sharing of
617 * repo info
618 */
619 json_object_set_string_member (object, "repo_name", commit->repo_name);
620 json_object_set_string_member (object, "repo_desc",
621 commit->repo_desc);
622 json_object_set_string_or_null_member (object, "repo_category",
623 commit->repo_category);
624 if (commit->device_name)
625 json_object_set_string_member (object, "device_name", commit->device_name);
626 if (commit->client_version)
627 json_object_set_string_member (object, "client_version", commit->client_version);
628
629 if (commit->encrypted)
630 json_object_set_string_member (object, "encrypted", "true");
631
632 if (commit->encrypted) {
633 json_object_set_int_member (object, "enc_version", commit->enc_version);
634 if (commit->enc_version >= 1)
635 json_object_set_string_member (object, "magic", commit->magic);
636 if (commit->enc_version >= 2)
637 json_object_set_string_member (object, "key", commit->random_key);
638 if (commit->enc_version >= 3)
639 json_object_set_string_member (object, "salt", commit->salt);
640 }
641 if (commit->no_local_history)
642 json_object_set_int_member (object, "no_local_history", 1);
643 if (commit->version != 0)
644 json_object_set_int_member (object, "version", commit->version);
645 if (commit->conflict)
646 json_object_set_int_member (object, "conflict", 1);
647 if (commit->new_merge)
648 json_object_set_int_member (object, "new_merge", 1);
649 if (commit->repaired)
650 json_object_set_int_member (object, "repaired", 1);
651
652 return object;
653 }
654
655 static SeafCommit *
commit_from_json_object(const char * commit_id,json_t * object)656 commit_from_json_object (const char *commit_id, json_t *object)
657 {
658 SeafCommit *commit = NULL;
659 const char *root_id;
660 const char *repo_id;
661 const char *creator_name = NULL;
662 const char *creator;
663 const char *desc;
664 gint64 ctime;
665 const char *parent_id, *second_parent_id;
666 const char *repo_name;
667 const char *repo_desc;
668 const char *repo_category;
669 const char *device_name;
670 const char *client_version;
671 const char *encrypted = NULL;
672 int enc_version = 0;
673 const char *magic = NULL;
674 const char *random_key = NULL;
675 const char *salt = NULL;
676 int no_local_history = 0;
677 int version = 0;
678 int conflict = 0, new_merge = 0;
679 int repaired = 0;
680
681 root_id = json_object_get_string_member (object, "root_id");
682 repo_id = json_object_get_string_member (object, "repo_id");
683 if (json_object_has_member (object, "creator_name"))
684 creator_name = json_object_get_string_or_null_member (object, "creator_name");
685 creator = json_object_get_string_member (object, "creator");
686 desc = json_object_get_string_member (object, "description");
687 if (!desc)
688 desc = "";
689 ctime = (guint64) json_object_get_int_member (object, "ctime");
690 parent_id = json_object_get_string_or_null_member (object, "parent_id");
691 second_parent_id = json_object_get_string_or_null_member (object, "second_parent_id");
692
693 repo_name = json_object_get_string_member (object, "repo_name");
694 if (!repo_name)
695 repo_name = "";
696 repo_desc = json_object_get_string_member (object, "repo_desc");
697 if (!repo_desc)
698 repo_desc = "";
699 repo_category = json_object_get_string_or_null_member (object, "repo_category");
700 device_name = json_object_get_string_or_null_member (object, "device_name");
701 client_version = json_object_get_string_or_null_member (object, "client_version");
702
703 if (json_object_has_member (object, "encrypted"))
704 encrypted = json_object_get_string_or_null_member (object, "encrypted");
705
706 if (encrypted && strcmp(encrypted, "true") == 0
707 && json_object_has_member (object, "enc_version")) {
708 enc_version = json_object_get_int_member (object, "enc_version");
709 magic = json_object_get_string_member (object, "magic");
710 }
711
712 if (enc_version >= 2)
713 random_key = json_object_get_string_member (object, "key");
714 if (enc_version >= 3)
715 salt = json_object_get_string_member (object, "salt");
716
717 if (json_object_has_member (object, "no_local_history"))
718 no_local_history = json_object_get_int_member (object, "no_local_history");
719
720 if (json_object_has_member (object, "version"))
721 version = json_object_get_int_member (object, "version");
722 if (json_object_has_member (object, "new_merge"))
723 new_merge = json_object_get_int_member (object, "new_merge");
724
725 if (json_object_has_member (object, "conflict"))
726 conflict = json_object_get_int_member (object, "conflict");
727
728 if (json_object_has_member (object, "repaired"))
729 repaired = json_object_get_int_member (object, "repaired");
730
731
732 /* sanity check for incoming values. */
733 if (!repo_id || !is_uuid_valid(repo_id) ||
734 !root_id || !is_object_id_valid(root_id) ||
735 !creator || strlen(creator) != 40 ||
736 (parent_id && !is_object_id_valid(parent_id)) ||
737 (second_parent_id && !is_object_id_valid(second_parent_id)))
738 return commit;
739
740 switch (enc_version) {
741 case 0:
742 break;
743 case 1:
744 if (!magic || strlen(magic) != 32)
745 return NULL;
746 break;
747 case 2:
748 if (!magic || strlen(magic) != 64)
749 return NULL;
750 if (!random_key || strlen(random_key) != 96)
751 return NULL;
752 break;
753 case 3:
754 if (!magic || strlen(magic) != 64)
755 return NULL;
756 if (!random_key || strlen(random_key) != 96)
757 return NULL;
758 if (!salt || strlen(salt) != 64)
759 return NULL;
760 break;
761 case 4:
762 if (!magic || strlen(magic) != 64)
763 return NULL;
764 if (!random_key || strlen(random_key) != 96)
765 return NULL;
766 if (!salt || strlen(salt) != 64)
767 return NULL;
768 break;
769 default:
770 seaf_warning ("Unknown encryption version %d.\n", enc_version);
771 return NULL;
772 }
773
774 char *creator_name_l = creator_name ? g_ascii_strdown (creator_name, -1) : NULL;
775 commit = seaf_commit_new (commit_id, repo_id, root_id,
776 creator_name_l, creator, desc, ctime);
777 g_free (creator_name_l);
778
779 commit->parent_id = parent_id ? g_strdup(parent_id) : NULL;
780 commit->second_parent_id = second_parent_id ? g_strdup(second_parent_id) : NULL;
781
782 commit->repo_name = g_strdup(repo_name);
783 commit->repo_desc = g_strdup(repo_desc);
784 if (encrypted && strcmp(encrypted, "true") == 0)
785 commit->encrypted = TRUE;
786 else
787 commit->encrypted = FALSE;
788 if (repo_category)
789 commit->repo_category = g_strdup(repo_category);
790 commit->device_name = g_strdup(device_name);
791 commit->client_version = g_strdup(client_version);
792
793 if (commit->encrypted) {
794 commit->enc_version = enc_version;
795 if (enc_version >= 1)
796 commit->magic = g_strdup(magic);
797 if (enc_version >= 2)
798 commit->random_key = g_strdup (random_key);
799 if (enc_version >= 3)
800 commit->salt = g_strdup(salt);
801 }
802 if (no_local_history)
803 commit->no_local_history = TRUE;
804 commit->version = version;
805 if (new_merge)
806 commit->new_merge = TRUE;
807 if (conflict)
808 commit->conflict = TRUE;
809 if (repaired)
810 commit->repaired = TRUE;
811
812 return commit;
813 }
814
815 static SeafCommit *
load_commit(SeafCommitManager * mgr,const char * repo_id,int version,const char * commit_id)816 load_commit (SeafCommitManager *mgr,
817 const char *repo_id,
818 int version,
819 const char *commit_id)
820 {
821 char *data = NULL;
822 int len;
823 SeafCommit *commit = NULL;
824 json_t *object = NULL;
825 json_error_t jerror;
826
827 if (!commit_id || strlen(commit_id) != 40)
828 return NULL;
829
830 if (seaf_obj_store_read_obj (mgr->obj_store, repo_id, version,
831 commit_id, (void **)&data, &len) < 0)
832 return NULL;
833
834 object = json_loadb (data, len, 0, &jerror);
835 if (!object) {
836 /* Perhaps the commit object contains invalid UTF-8 character. */
837 if (data[len-1] == 0)
838 clean_utf8_data (data, len - 1);
839 else
840 clean_utf8_data (data, len);
841
842 object = json_loadb (data, len, 0, &jerror);
843 if (!object) {
844 if (jerror.text)
845 seaf_warning ("Failed to load commit json object: %s.\n", jerror.text);
846 else
847 seaf_warning ("Failed to load commit json object.\n");
848 goto out;
849 }
850 }
851
852 commit = commit_from_json_object (commit_id, object);
853 if (commit)
854 commit->manager = mgr;
855
856 out:
857 if (object) json_decref (object);
858 g_free (data);
859
860 return commit;
861 }
862
863 static int
save_commit(SeafCommitManager * manager,const char * repo_id,int version,SeafCommit * commit)864 save_commit (SeafCommitManager *manager,
865 const char *repo_id,
866 int version,
867 SeafCommit *commit)
868 {
869 json_t *object = NULL;
870 char *data;
871 gsize len;
872
873 if (seaf_obj_store_obj_exists (manager->obj_store,
874 repo_id, version,
875 commit->commit_id))
876 return 0;
877
878 object = commit_to_json_object (commit);
879
880 data = json_dumps (object, 0);
881 len = strlen (data);
882
883 json_decref (object);
884
885 #ifdef SEAFILE_SERVER
886 if (seaf_obj_store_write_obj (manager->obj_store,
887 repo_id, version,
888 commit->commit_id,
889 data, (int)len, TRUE) < 0) {
890 g_free (data);
891 return -1;
892 }
893 #else
894 if (seaf_obj_store_write_obj (manager->obj_store,
895 repo_id, version,
896 commit->commit_id,
897 data, (int)len, FALSE) < 0) {
898 g_free (data);
899 return -1;
900 }
901 #endif
902 free (data);
903
904 return 0;
905 }
906
907 static void
delete_commit(SeafCommitManager * mgr,const char * repo_id,int version,const char * id)908 delete_commit (SeafCommitManager *mgr,
909 const char *repo_id,
910 int version,
911 const char *id)
912 {
913 seaf_obj_store_delete_obj (mgr->obj_store, repo_id, version, id);
914 }
915
916 int
seaf_commit_manager_remove_store(SeafCommitManager * mgr,const char * store_id)917 seaf_commit_manager_remove_store (SeafCommitManager *mgr,
918 const char *store_id)
919 {
920 return seaf_obj_store_remove_store (mgr->obj_store, store_id);
921 }
922