add copy/move detection to push

Sun, 07 Apr 2019 09:07:33 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 07 Apr 2019 09:07:33 +0200
changeset 550
8c700eae3eb8
parent 549
af91d3c96994
child 551
99ef8202cd82

add copy/move detection to push
only detection is implemented, COPY/MOVE operation is not implemented yet

dav/db.c file | annotate | diff | comparison | revisions
dav/db.h file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
--- a/dav/db.c	Sat Apr 06 14:25:07 2019 +0200
+++ b/dav/db.c	Sun Apr 07 09:07:33 2019 +0200
@@ -663,3 +663,18 @@
     }
     free(part);
 }
+
+UcxMap* create_hash_index(SyncDatabase *db) {
+    UcxMap *hmap = ucx_map_new(db->resources->count + 64);
+    
+    UcxMapIterator i = ucx_map_iterator(db->resources);
+    UcxKey key;
+    LocalResource *res;
+    UCX_MAP_FOREACH(key, res, i) {
+        if(res->hash) {
+            ucx_map_cstr_put(hmap, res->hash, res);
+        }
+    }
+    
+    return hmap;
+}
--- a/dav/db.h	Sat Apr 06 14:25:07 2019 +0200
+++ b/dav/db.h	Sun Apr 07 09:07:33 2019 +0200
@@ -80,6 +80,9 @@
     
     DavBool keep;
     DavBool restore;
+    
+    DavBool isnew;
+    char *origin;
 };
 
 struct FilePart {
@@ -101,6 +104,8 @@
 
 void filepart_free(FilePart *part);
 
+UcxMap* create_hash_index(SyncDatabase *db);
+
 LocalResource* process_resource(xmlTextReaderPtr reader);
 LocalResource* process_conflict(xmlTextReaderPtr reader);
 
--- a/dav/sync.c	Sat Apr 06 14:25:07 2019 +0200
+++ b/dav/sync.c	Sun Apr 07 09:07:33 2019 +0200
@@ -1496,15 +1496,23 @@
     
     DavBool remove_file = cmd_getoption(a, "remove") ? 1 : 0;
     
+    UcxMap *db_hashes = NULL;
+    if(dir->hashing) {
+        db_hashes = create_hash_index(db);
+    }
+    
     int sync_success = 0;
     int sync_delete = 0;
     int sync_skipped = 0;
     int sync_error = 0;
     
-    UcxList *ls_put = NULL;
+    UcxList *ls_new = NULL;
+    UcxList *ls_modified = NULL;
     UcxList *ls_conflict = NULL;
     UcxList *ls_update = NULL;
     UcxList *ls_delete = NULL;
+    UcxList *ls_move = NULL;
+    UcxList *ls_copy = NULL;
     
     // upload all changed files
     //UcxList *resources = cmd_getoption(a, "read") ?
@@ -1528,7 +1536,7 @@
             }
         }
         
-        // we need a fast file lookup map later
+        // we need a fast file lookup map later to detect deleted files
         ucx_map_cstr_put(resources_map, local_res->path, local_res);
         
         // dynamic tag filter
@@ -1558,12 +1566,58 @@
                     restore_removed,
                     restore_modified);
         if(is_changed) {
-            ls_put = ucx_list_append(ls_put, local_res);
+            if(local_res->isnew) {
+                ls_new = ucx_list_append(ls_new, local_res);
+            } else {
+                ls_modified = ucx_list_append(ls_modified, local_res);
+            }
         } else if(local_res->metadata_updated) {
             ls_update = ucx_list_append(ls_update, local_res);
         }
     }
     
+    if(dir->hashing) {
+        // calculate hashes of all new files and check if a file
+        // was moved or is a copy
+        UcxList *elm = ls_new;
+        while(elm) {
+            LocalResource *local = elm->data;
+            UcxList *prev = elm->prev;
+            UcxList *next = elm->next;
+            char *local_path = util_concat_path(dir->path, local->path);
+            char *hash = util_file_hash(local_path);
+            // check if a file with this hash already exists
+            LocalResource *origin = ucx_map_cstr_get(db_hashes, hash);
+            if(origin) {
+                local->origin = strdup(origin->path);
+                // the file is a copied/moved file
+                // check if the file is in the resources_map, because then
+                // it still exists
+                if(ucx_map_cstr_get(resources_map, origin->path)) {
+                    ls_copy = ucx_list_append(ls_copy, local);
+                } else {
+                    ls_move = ucx_list_append(ls_move, local);
+                    // put file in resources_map to prevent deletion
+                    ucx_map_cstr_put(resources_map, origin->path, local);
+                }
+                // remove list elemend from ls_new
+                if(prev) {
+                    prev->next = next;
+                } else {
+                    ls_new = next;
+                }
+                if(next) {
+                    next->prev = prev;
+                }
+            }
+            
+            free(hash);
+            free(local_path);
+            
+            elm = next;
+        }
+    }
+    
     // find all deleted files and cleanup the database
     UcxMapIterator i = ucx_map_iterator(db->resources);
     LocalResource *local;
@@ -1585,7 +1639,7 @@
         if(!ucx_map_get(resources_map, key)) {
             // The current LocalResource is in the database but doesn't exist
             // in the filesystem anymore. This means the file was deleted
-            // and should be deleted on server
+            // and should be deleted on the server
             if(!archive) {
                 ls_delete = ucx_list_append(ls_delete, local);
             } else {
@@ -1605,9 +1659,11 @@
     //
     
     // upload changed files 
+    ls_modified = ucx_list_concat(ls_new, ls_modified);
+    
     int ret = 0;
     int error = 0;
-    for(UcxList *elm=ls_put;elm && !sync_shutdown;elm=elm->next) {
+    for(UcxList *elm=ls_modified;elm && !sync_shutdown;elm=elm->next) {
         LocalResource *local_res = elm->data;
         
         DavResource *res = dav_resource_new(sn, local_res->path);
@@ -2254,6 +2310,7 @@
         res->finfo_updated = 1;
         res->xattr_updated = 1;
         res->metadata_updated = 1;
+        res->isnew = 1;
     }
     return 1;
 }
@@ -3267,6 +3324,14 @@
                     local->etag = NULL;
                 }
                 
+                if(!issplit && dir->hashing) {
+                    if(local->hash) {
+                        free(local->hash);
+                    }
+                    // TODO: calculate hash on upload
+                    local->hash = util_file_hash(local_path);
+                }
+                
                 if(dav_get_string_property(up_res, "idav:status")) {
                     sync_remove_status(up_res);
                 }

mercurial