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