implement copy/move for dav-sync pull

Fri, 12 Apr 2019 12:42:41 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 12 Apr 2019 12:42:41 +0200
changeset 567
b0ce8b27978b
parent 566
9a88920b15d8
child 568
a81cad6bb377

implement copy/move for dav-sync pull

dav/db.c file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
dav/sync.h file | annotate | diff | comparison | revisions
--- a/dav/db.c	Fri Apr 12 11:13:16 2019 +0200
+++ b/dav/db.c	Fri Apr 12 12:42:41 2019 +0200
@@ -672,7 +672,7 @@
     free(res);
 }
 
-char* nullstrdup(const char *s) {
+static char* nullstrdup(const char *s) {
     return s ? strdup(s) : NULL;
 }
 
--- a/dav/sync.c	Fri Apr 12 11:13:16 2019 +0200
+++ b/dav/sync.c	Fri Apr 12 12:42:41 2019 +0200
@@ -101,6 +101,10 @@
     return strcmp(s1, s2);
 }
 
+static char* nullstrdup(const char *s) {
+    return s ? strdup(s) : NULL;
+}
+
 static void nullfree(void *p) {
     if(p) {
         free(p);
@@ -514,6 +518,11 @@
     }
     remove_deleted_conflicts(dir, db);
     
+    UcxMap *hashes = NULL;
+    if(dir->hashing) {
+        hashes = create_hash_index(db);
+    }
+    
     DavSession *sn = create_session(ctx, repo, dir->collection);
     ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db);
     if (cmd_getoption(a, "verbose")) {
@@ -587,13 +596,13 @@
     
     UcxList *res_modified = NULL;
     UcxList *res_new = NULL;
-    UcxList *res_moved = NULL;
-    UcxList *res_copied = NULL;
+    UcxList *res_moved = NULL; // type: MovedFile
+    UcxList *res_copied = NULL; // type: MovedFile
     UcxList *res_conflict = NULL;
     UcxList *res_mkdir = NULL;
     UcxList *res_metadata = NULL;
     UcxList *res_broken = NULL;
-    UcxMap  *lres_removed = ucx_map_new(16); // contains LocalResource*
+    UcxMap  *lres_removed = ucx_map_new(16); // type: LocalResource*
     
     //UcxMap *svrres = ucx_map_new(db->resources->count);
     UcxMap *dbres = ucx_map_clone(db->resources, NULL, NULL);
@@ -718,6 +727,78 @@
         ucx_map_cstr_put(conflicts, res->path, res);
     }
     
+    if(dir->hashing) {
+        // check for moved/copied files
+        UcxList *elm = res_new;
+        UcxList *prev = NULL;
+        UcxList *next = NULL;
+        struct stat s;
+        for(;elm;elm=next) {
+            DavResource *res = elm->data;
+            prev = elm->prev;
+            next = elm->next;
+            
+            char *hash = sync_get_content_hash(res);
+            if(!hash) {
+                continue;
+            }
+            
+            LocalResource *local = ucx_map_cstr_get(hashes, hash);
+            if(!local) {
+                continue;
+            }
+            
+            char *local_path = util_concat_path(dir->path, local->path);
+            int staterr = stat(local_path, &s);
+            free(local_path);
+            if(staterr) {
+                // origin doesn't exist or is inaccessible
+                continue;
+            }
+            
+            MovedFile *mf = malloc(sizeof(MovedFile));
+            mf->content = local;
+            mf->resource = res;
+            if(ucx_map_cstr_get(lres_removed, local->path)) {
+                mf->copy = FALSE;
+            } else {
+                mf->copy = TRUE;
+            }
+            
+            res_moved = ucx_list_append(res_moved, mf);
+            
+            // remove item from res_new
+            if(prev) {
+                prev->next = next;
+            } else {
+                res_new = next;
+            }
+            if(next) {
+                next->prev = prev;
+            }
+        }
+    }
+    
+    // do copy/move operations
+    UCX_FOREACH(elm, res_moved) {
+        MovedFile *mf = elm->data;
+        if(sync_shutdown) {
+            break;
+        }
+        
+        DavBool issplit = dav_get_property_ns(mf->resource, DAV_NS, "split") ? 1 : 0;  
+        if(ucx_map_cstr_get(conflicts, mf->resource->path)) {
+            rename_conflict_file(dir, db, mf->resource->path, issplit);
+            sync_conflict++;
+        }
+        
+        // move file
+        if(sync_move_resource(a, dir, mf->resource, mf->content, mf->copy, db, &sync_success)) {
+            fprintf(stderr, "%s failed: %s\n", mf->copy?"copy":"move", mf->resource->path);
+            sync_error++;
+        }
+    }
+    
     // download all new, modified and conflict files
     UcxList *download = ucx_list_concat(res_modified, res_conflict);
     download = ucx_list_concat(res_new, download);
@@ -1088,7 +1169,80 @@
     return updates;
 }
 
+int copy_file(const char *from, const char *to) {
+    FILE *in = sys_fopen(from, "rb");
+    if(!in) {
+        return 1;
+    }
+    FILE *out = sys_fopen(to, "wb");
+    if(!out) {
+        fclose(in);
+        return 1;
+    }
+    
+    ucx_stream_copy(in, out, (read_func)fread, (write_func)fwrite);
+    fclose(in);
+    fclose(out);
+    
+    return 0;
+}
 
+typedef int (*renamefunc)(const char*,const char*);
+
+int sync_move_resource(
+        CmdArgs *a,
+        SyncDirectory *dir,
+        DavResource *res,
+        LocalResource *content,
+        DavBool copy,
+        SyncDatabase *db,
+        int *counter)
+{
+    renamefunc fn = copy ? copy_file : sys_rename;
+    
+    char *new_path = util_concat_path(dir->path, res->path);
+    char *old_path = util_concat_path(dir->path, content->path);
+    
+    printf("%s: %s -> %s\n", copy?"copy":"move", content->path, res->path);
+    if(fn(old_path, new_path)) {
+        free(new_path);
+        free(old_path);
+        return 1;
+    }
+    (*counter)++;
+    
+    char *etag = dav_get_string_property(res, "D:getetag");
+    char *content_hash = sync_get_content_hash(res);
+    
+    LocalResource *local = local_resource_copy(content, res->path);
+    ucx_map_cstr_put(db->resources, local->path, local);
+    
+    if(sync_store_metadata(dir, new_path, local, res)) {
+        fprintf(stderr, "Cannot store metadata: %s\n", res->path);
+    }
+    
+    if(local->etag) {
+        free(local->etag);
+    }
+    if(local->hash) {
+        free(local->hash);
+    }
+    
+    SYS_STAT s;
+    if(sys_stat(new_path, &s)) {
+        fprintf(stderr,
+                "Cannot stat file %s: %s\n", new_path, strerror(errno));
+    }
+    
+    // set metadata from stat
+    local->etag = nullstrdup(etag);
+    local->hash = nullstrdup(content_hash);
+    
+    sync_set_metadata_from_stat(local, &s);
+    local->skipped = FALSE;
+    
+    return 0;
+}
 
 int sync_get_resource(
         CmdArgs *a,
@@ -1286,26 +1440,6 @@
     return ret;
 }
 
-int copy_file(const char *from, const char *to) {
-    FILE *in = sys_fopen(from, "rb");
-    if(!in) {
-        return 1;
-    }
-    FILE *out = sys_fopen(to, "wb");
-    if(!out) {
-        fclose(in);
-        return 1;
-    }
-    
-    ucx_stream_copy(in, out, (read_func)fread, (write_func)fwrite);
-    fclose(in);
-    fclose(out);
-    
-    return 0;
-}
-
-typedef int (*renamefunc)(const char*,const char*);
-
 void rename_conflict_file(SyncDirectory *dir, SyncDatabase *db, char *path, DavBool copy) {
     char *local_path = create_local_path(dir, path);
     char *parent = util_parent_path(local_path);
@@ -1797,6 +1931,8 @@
                 // upload with put
                 printf("put: %s\n", local->path);
                 err = sync_put_resource(dir, res, local, &sync_success);
+                
+                // TODO: if move, delete old resource
             } else {
                 printf("%s: %s -> %s\n", copy ? "copy":"move", local->origin->path, local->path);
                 err = sync_move_remote_resource(
@@ -2419,7 +2555,7 @@
         char *etag = dav_get_string_property(remote, "D:getetag");
         char *hash = sync_get_content_hash(remote);
         if(hash && res->hash) {
-            if(!strcmp(hash, res->hash)) {
+            if(strcmp(hash, res->hash)) {
                 ret = 1;
             }
         } else if(!res->etag) {
--- a/dav/sync.h	Fri Apr 12 11:13:16 2019 +0200
+++ b/dav/sync.h	Fri Apr 12 12:42:41 2019 +0200
@@ -66,6 +66,12 @@
     char *xattr;
 } MetadataHashes;
 
+typedef struct MovedFile {
+    DavResource *resource;
+    LocalResource *content;
+    DavBool copy;
+} MovedFile;
+
 enum RemoteChangeType {
     REMOTE_NO_CHANGE = 0,
     REMOTE_CHANGE_MODIFIED,
@@ -100,6 +106,14 @@
         SyncDatabase *db);
 
 void sync_set_metadata_from_stat(LocalResource *local, struct stat *s);
+int sync_move_resource(
+        CmdArgs *a,
+        SyncDirectory *dir,
+        DavResource *res,
+        LocalResource *content,
+        DavBool copy,
+        SyncDatabase *db,
+        int *counter);
 int sync_get_resource(
         CmdArgs *a,
         SyncDirectory *dir,

mercurial