# HG changeset patch # User Olaf Wintermann # Date 1555065761 -7200 # Node ID b0ce8b27978b9701b1a2a385ab0e1ff0df0261bd # Parent 9a88920b15d8aec3a3e1375f447d6261612a9d04 implement copy/move for dav-sync pull diff -r 9a88920b15d8 -r b0ce8b27978b dav/db.c --- 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; } diff -r 9a88920b15d8 -r b0ce8b27978b dav/sync.c --- 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) { diff -r 9a88920b15d8 -r b0ce8b27978b dav/sync.h --- 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,