# HG changeset patch # User Olaf Wintermann # Date 1540645513 -7200 # Node ID d94c4fd35c215b045fd31cfce7edb03e8e2a3481 # Parent fb69eae42ef0bc57421e26aa2248bb7f9d9c64b9 adds dav-sync restore command diff -r fb69eae42ef0 -r d94c4fd35c21 dav/scfg.c --- 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); } diff -r fb69eae42ef0 -r d94c4fd35c21 dav/scfg.h --- 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" diff -r fb69eae42ef0 -r d94c4fd35c21 dav/sync.c --- 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 ] \n"); fprintf(stderr, " push [-cldrRM] [-t ] \n"); fprintf(stderr, " archive [-cldRM] [-t ] \n"); + fprintf(stderr, " restore [-ld] \n"); fprintf(stderr, " resolve-conflicts \n"); fprintf(stderr, " delete-conflicts \n"); fprintf(stderr, " trash-info \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; diff -r fb69eae42ef0 -r d94c4fd35c21 dav/sync.h --- 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);