adds dav-sync restore command

Sat, 27 Oct 2018 15:05:13 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 27 Oct 2018 15:05:13 +0200
changeset 490
d94c4fd35c21
parent 489
fb69eae42ef0
child 491
fdc2fb090cc7

adds dav-sync restore command

dav/scfg.c file | annotate | diff | comparison | revisions
dav/scfg.h file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
dav/sync.h file | annotate | diff | comparison | revisions
--- a/dav/scfg.c	Sat Oct 27 12:16:26 2018 +0200
+++ b/dav/scfg.c	Sat Oct 27 15:05:13 2018 +0200
@@ -264,7 +264,8 @@
     UcxList *exclude = NULL;
     UcxList *tagfilter = NULL;
     int max_retry = 0;
-    int allow_cmd = SYNC_CMD_PULL | SYNC_CMD_PUSH | SYNC_CMD_ARCHIVE;
+    int allow_cmd = SYNC_CMD_PULL | SYNC_CMD_PUSH
+                  | SYNC_CMD_ARCHIVE | SYNC_CMD_RESTORE;
     bool backuppull = false;
     bool lockpull = false;
     bool lockpush = false;
@@ -323,6 +324,8 @@
                         cmds |= SYNC_CMD_PUSH;
                     } else if(!strcmp(cmd, "archive")) {
                         cmds |= SYNC_CMD_ARCHIVE;
+                    } else if(!strcmp(cmd, "restore")) {
+                        cmds |= SYNC_CMD_RESTORE;
                     }
                     cmd = strtok(NULL, delims);
                 }
--- a/dav/scfg.h	Sat Oct 27 12:16:26 2018 +0200
+++ b/dav/scfg.h	Sat Oct 27 15:05:13 2018 +0200
@@ -44,6 +44,7 @@
 #define SYNC_CMD_PULL    1
 #define SYNC_CMD_PUSH    2
 #define SYNC_CMD_ARCHIVE 4
+#define SYNC_CMD_RESTORE 8
     
 #define DEFAULT_TAG_XATTR "tags"
 #define MACOS_TAG_XATTR "com.apple.metadata:_kMDItemUserTags"
--- a/dav/sync.c	Sat Oct 27 12:16:26 2018 +0200
+++ b/dav/sync.c	Sat Oct 27 15:05:13 2018 +0200
@@ -123,6 +123,10 @@
             tid = start_sighandler(&mutex);
             ret = cmd_push(args, TRUE);
             stop_sighandler(&mutex, tid);
+        } else if(!strcmp(cmd, "restore")) {
+            tid = start_sighandler(&mutex);
+            ret = cmd_restore(args);
+            stop_sighandler(&mutex, tid);
         } else if(!strcmp(cmd, "resolve-conflicts")) {
             ret = cmd_resolve_conflicts(args);
         } else if(!strcmp(cmd, "delete-conflicts")) {
@@ -173,6 +177,7 @@
     fprintf(stderr, "        pull [-cldr] [-t <tags>] <directory>\n");
     fprintf(stderr, "        push [-cldrRM] [-t <tags>] <directory>\n");
     fprintf(stderr, "        archive [-cldRM] [-t <tags>] <directory>\n");
+    fprintf(stderr, "        restore [-ld] <directory>\n");
     fprintf(stderr, "        resolve-conflicts <directory>\n");
     fprintf(stderr, "        delete-conflicts <directory>\n");
     fprintf(stderr, "        trash-info <directory>\n");
@@ -543,7 +548,7 @@
             }
             
             // download the resource
-            if(!sync_shutdown && sync_get_resource(a, dir, res, db, &sync_success)) {
+            if(!sync_shutdown && sync_get_resource(a, dir, res, db, FALSE, &sync_success)) {
                 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path);
                 sync_error++;
             }
@@ -651,6 +656,7 @@
         SyncDirectory *dir,
         DavResource *res,
         SyncDatabase *db,
+        DavBool force,
         int *counter)
 {
     int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection
@@ -661,48 +667,50 @@
     char *etag = dav_get_string_property(res, "D:getetag");
     SYS_STAT s;
     memset(&s, 0, sizeof(SYS_STAT));
-    if(local && !res->iscollection) {
-        int exists = 1;
-        if(sys_stat(local_path, &s)) {
-            // Ignore the fact, that the file is locally removed. If the
-            // server has an updated version, we read the file or the
-            // next push will delete it on the server.
-            if(errno != ENOENT) {
-                fprintf(stderr, "Cannot stat file: %s\n", local_path);
-                free(local_path);
-                return -1;
-            } else {
-                exists = 0;
-            }
-        }
-              
-        if(local->etag) {
-            sstr_t e = sstr(etag);
-            if(sstrprefix(e, S("W/"))) {
-                e = sstrsubs(e, 2);
+    if(!force) {
+        if(local && !res->iscollection) {
+            int exists = 1;
+            if(sys_stat(local_path, &s)) {
+                // Ignore the fact, that the file is locally removed. If the
+                // server has an updated version, we read the file or the
+                // next push will delete it on the server.
+                if(errno != ENOENT) {
+                    fprintf(stderr, "Cannot stat file: %s\n", local_path);
+                    free(local_path);
+                    return -1;
+                } else {
+                    exists = 0;
+                }
             }
-            if(!strcmp(e.ptr, local->etag)) {
-                // resource is already up-to-date on the client
-                sync_store_tags(dir, local_path, local, res);
-                free(local_path);
-                return 0;
+
+            if(local->etag) {
+                sstr_t e = sstr(etag);
+                if(sstrprefix(e, S("W/"))) {
+                    e = sstrsubs(e, 2);
+                }
+                if(!strcmp(e.ptr, local->etag)) {
+                    // resource is already up-to-date on the client
+                    sync_store_tags(dir, local_path, local, res);
+                    free(local_path);
+                    return 0;
+                }
             }
-        }
-        
-        if(cdt && exists && s.st_mtime != local->last_modified) {
-            // file modified on the server and on the client
-            rename_conflict_file(dir, db, local->path);
-        }
-    } else {
-        if(sys_stat(local_path, &s)) {
-            if(errno != ENOENT) {
-                fprintf(stderr, "Cannot stat file: %s\n", local_path);
+
+            if(cdt && exists && s.st_mtime != local->last_modified) {
+                // file modified on the server and on the client
+                rename_conflict_file(dir, db, local->path);
             }
-        } else if(S_ISDIR(s.st_mode)) {
-            //fprintf(stderr, "Error: file %s is a directory\n", local_path);
-        } else if(cdt) {
-            // rename file on conflict
-            rename_conflict_file(dir, db, res->path);
+        } else {
+            if(sys_stat(local_path, &s)) {
+                if(errno != ENOENT) {
+                    fprintf(stderr, "Cannot stat file: %s\n", local_path);
+                }
+            } else if(S_ISDIR(s.st_mode)) {
+                //fprintf(stderr, "Error: file %s is a directory\n", local_path);
+            } else if(cdt) {
+                // rename file on conflict
+                rename_conflict_file(dir, db, res->path);
+            }
         }
     }
       
@@ -1289,6 +1297,159 @@
     return ret;
 }
 
+static int localres_cmp_path(LocalResource *a, LocalResource *b, void *n) {
+    return strcmp(a->path, b->path);
+}
+
+int cmd_restore(CmdArgs *a) {
+    SyncDirectory *dir = scfg_get_dir(a->argv[0]);
+    if(!dir) {
+        fprintf(stderr, "Unknown sync dir: %s\n", a->argv[0]);
+        return -1;
+    }
+    if(scfg_check_dir(dir)) {
+        return -1;
+    }
+    
+    if((dir->allow_cmd & SYNC_CMD_RESTORE) != SYNC_CMD_RESTORE) {
+        fprintf(stderr, "Command ''restore'' is not allowed for this sync dir\n");
+        print_allowed_cmds(dir);
+        return -1;
+    }
+    
+    SyncDatabase *db = load_db(dir->database);
+    if(!db) {
+        fprintf(stderr, "Cannot load database file: %s\n", dir->database);
+        return -1;
+    }
+    remove_deleted_conflicts(dir, db);
+    
+    UcxList *modified = NULL;
+    UcxList *deleted = NULL;
+    
+    // iterate over all db resources and check if any resource is
+    // modified or deleted
+    UcxMapIterator i = ucx_map_iterator(db->resources);
+    LocalResource *resource;
+    UCX_MAP_FOREACH(key, resource, i) {
+        char *file_path = util_concat_path(dir->path, resource->path);
+        SYS_STAT s;
+        if(sys_stat(file_path, &s)) {
+            if(errno == ENOENT) {
+                deleted = ucx_list_prepend(deleted, resource);
+            } else {
+                fprintf(stderr, "Cannot stat file: %s\n", file_path);
+                perror("");
+            }
+        } else if(!resource->isdirectory && !S_ISDIR(s.st_mode)) {
+            if(resource->last_modified != s.st_mtime || resource->size != s.st_size) {
+                modified = ucx_list_prepend(modified, resource);
+            }
+        }
+        
+        free(file_path);
+    }
+    
+    int ret = 0;
+    
+    // create DavSession
+    Repository *repo = get_repository(sstr(dir->repository));
+    if(!repo) {
+        fprintf(stderr, "Unkown repository %s\n", dir->name);
+        return -1;
+    }
+    char *new_url = NULL;
+    if(dir->collection) {
+        new_url = util_concat_path(repo->url, dir->collection);
+    }
+    DavSession *sn = create_session(ctx, repo, new_url ? new_url : repo->url);
+    ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db);
+    if(new_url) {
+        free(new_url);
+    }
+    if (cmd_getoption(a, "verbose")) {
+        curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
+        curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
+    }
+    
+    // lock repository
+    char *locktokenfile = NULL;
+    DavBool locked = FALSE;
+    DavResource *root = dav_resource_new(sn, "/");
+    root->iscollection = TRUE;
+    if((dir->lockpush || cmd_getoption(a, "lock")) && !cmd_getoption(a, "nolock")) {
+        if(dav_lock_t(root, dir->lock_timeout)) {
+            print_resource_error(sn, "/");
+            dav_session_destroy(sn);
+            fprintf(stderr, "Abort\n");
+            return -1;
+        }
+        DavLock *lock = dav_get_lock(sn, "/");
+        if(lock) {
+            printf("Lock-Token: %s\n", lock->token);
+        }
+        locked = TRUE;
+        locktokenfile = create_locktoken_file(dir->name, lock->token);
+    }
+    
+    int sync_success = 0;
+    int sync_error = 0;
+    
+    UcxList *resources = ucx_list_concat(modified, deleted);
+    resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL);
+    
+    UCX_FOREACH(elm, resources) {
+        LocalResource *resource = elm->data;
+        
+        DavResource *res = dav_get(sn, resource->path, "idav:status,D:getetag");
+        
+        char *status = dav_get_string_property(res, "idav:status");
+        if(status && !strcmp(status, "broken")) {
+            continue;
+        }
+        
+        // download the resource
+        if(!sync_shutdown && sync_get_resource(a, dir, res, db, TRUE, &sync_success)) {
+            fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path);
+            sync_error++;
+        }
+    }
+    
+    // unlock repository
+    if(locked) {
+        if(dav_unlock(root)) {
+            print_resource_error(sn, "/");
+            ret = -1;
+        } else {
+            locked = FALSE;
+        }
+    }
+    
+    // store db
+    if(store_db(db, dir->database)) {
+        fprintf(stderr, "Cannot store sync db\n");
+        ret = -2;
+    }
+    
+    // cleanup
+    dav_session_destroy(sn);
+    
+    if(!locked && locktokenfile) {
+        remove(locktokenfile);
+    }
+    
+    // Report
+    if(ret != -2) {
+        char *str_success = sync_success == 1 ? "file" : "files";
+        char *str_error = sync_error == 1 ? "error" : "errors";
+        printf("Result: %d %s pulled, %d %s\n",
+                sync_success, str_success,
+                sync_error, str_error);
+    }
+    
+    return 0;
+}
+
 UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) {
     UcxList *resources = NULL;
     
--- a/dav/sync.h	Sat Oct 27 12:16:26 2018 +0200
+++ b/dav/sync.h	Sat Oct 27 15:05:13 2018 +0200
@@ -67,12 +67,14 @@
 
 int cmd_pull(CmdArgs *args);
 int cmd_push(CmdArgs *args, DavBool archive);
+int cmd_restore(CmdArgs *args);
 
 int sync_get_resource(
         CmdArgs *a,
         SyncDirectory *dir,
         DavResource *res,
         SyncDatabase *db,
+        DavBool force,
         int *counter);
 int sync_remove_local_resource(SyncDirectory *dir, LocalResource *res);
 void sync_remove_local_directory(SyncDirectory *dir, LocalResource *res);

mercurial