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