Sun, 17 Mar 2019 18:11:31 +0100
adds metadata support to dav-sync pull
dav/finfo.c | file | annotate | diff | comparison | revisions | |
dav/finfo.h | file | annotate | diff | comparison | revisions | |
dav/main.c | file | annotate | diff | comparison | revisions | |
dav/scfg.c | file | annotate | diff | comparison | revisions | |
dav/sync.c | file | annotate | diff | comparison | revisions | |
dav/sync.h | file | annotate | diff | comparison | revisions | |
libidav/webdav.h | file | annotate | diff | comparison | revisions | |
libidav/xml.c | file | annotate | diff | comparison | revisions |
--- a/dav/finfo.c Sun Mar 17 15:00:48 2019 +0100 +++ b/dav/finfo.c Sun Mar 17 18:11:31 2019 +0100 @@ -32,15 +32,17 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <errno.h> #include <sys/stat.h> #include <ucx/string.h> +#include <ucx/list.h> #include <libidav/crypto.h> #include <libidav/utils.h> #include "libxattr.h" -uint32_t parse_finfo(const char *str, char **error) { +uint32_t parse_finfo_settings(const char *str, char **error) { scstr_t s = scstr(str); if(!sstrcmp(s, SC("*")) || !sstrcmp(s, SC("a")) || !sstrcmp(s, SC("all"))) { @@ -127,6 +129,65 @@ return 0; } +XAttributes* xml_get_attributes(DavXmlNode *xml) { + UcxList *names = NULL; + UcxList *values = NULL; + + size_t count = 0; + + char *hash = NULL; + + DavXmlNode *node = xml; + while(node) { + if(node->type == DAV_XML_ELEMENT) { + if(!strcmp(node->name, "hash")) { + hash = dav_xml_getstring(node->children); + } else if(!strcmp(node->name, "xattr")) { + char *xattr_name = dav_xml_get_attr(node, "name"); + if(xattr_name) { + names = ucx_list_append(names, strdup(xattr_name)); + + char *text = dav_xml_getstring(node->children); + if(!text) { + text = ""; + } + + int len = 0; + char *val = util_base64decode_len(text, &len); + + sstr_t *value = malloc(sizeof(sstr_t)); + value->ptr = val; + value->length = len; + + values = ucx_list_append(values, value); + + count++; + } + } + } + + node = node->next; + } + + XAttributes *attributes = NULL; + if(count > 0) { + attributes = calloc(1, sizeof(XAttributes)); + attributes->hash = hash ? strdup(hash) : NULL; + attributes->nattr = count; + int i=0; + UCX_FOREACH(elm, names) { + attributes->names[i] = elm->data; + i++; + } + i=0; + UCX_FOREACH(elm, values) { + attributes->values[i] = *(sstr_t*)elm->data; + i++; + } + } + return attributes; +} + XAttributes* file_get_attributes(const char *path) { ssize_t nelm = 0; char **attributes = xattr_list(path, &nelm); @@ -212,3 +273,44 @@ } free(xattr); } + +char* get_xattr_hash(DavXmlNode *finfo) { + DavXmlNode *node = finfo; + while(node) { + if(node->type == DAV_XML_ELEMENT && !strcmp(node->name, "hash")) { + return dav_xml_getstring(node->children); + } + node = node->next; + } + return NULL; +} + +void finfo_get_values(DavXmlNode *xml, FileInfo *outval) { + memset(outval, 0, sizeof(FileInfo)); + DavXmlNode *node = xml; + while(node) { + if(node->type == DAV_XML_ELEMENT) { + if(!strcmp(node->name, "mtime")) { + char *mtime = dav_xml_getstring(node->children); + if(mtime) { + outval->last_modified = util_parse_lastmodified(mtime); + outval->date_set = TRUE; + } + } else if(!strcmp(node->name, "mode")) { + char *mode_str = dav_xml_getstring(node->children); + if(mode_str) { + char *end; + errno = 0; + long int mode = strtol(mode_str, &end, 8); + if(errno == 0) { + mode &= 07777; + outval->mode = mode; + outval->mode_set = TRUE; + } + } + } + } + node = node->next; + } + +}
--- a/dav/finfo.h Sun Mar 17 15:00:48 2019 +0100 +++ b/dav/finfo.h Sun Mar 17 18:11:31 2019 +0100 @@ -49,15 +49,31 @@ char *hash; } XAttributes; -uint32_t parse_finfo(const char *str, char **unknown); +typedef struct FileInfo { + time_t last_modified; + mode_t mode; + uid_t uid; + gid_t gid; + DavBool date_set; + DavBool mode_set; + DavBool uid_set; + DavBool gid_set; +} FileInfo; + +uint32_t parse_finfo_settings(const char *str, char **unknown); int resource_set_finfo(const char *path, DavResource *res, uint32_t finfo); int resource_set_finfo_s(struct stat *s, DavResource *res, uint32_t finfo); +XAttributes* xml_get_attributes(DavXmlNode *xml); XAttributes* file_get_attributes(const char *path); int resource_set_xattr(DavResource *res, XAttributes *xattr); void xattributes_free(XAttributes *xattr); +char* get_xattr_hash(DavXmlNode *finfo); + +void finfo_get_values(DavXmlNode *xml, FileInfo *outval); + #ifdef __cplusplus } #endif
--- a/dav/main.c Sun Mar 17 15:00:48 2019 +0100 +++ b/dav/main.c Sun Mar 17 18:11:31 2019 +0100 @@ -1259,7 +1259,7 @@ char *finfo_str = cmd_getoption(a, "finfo"); uint32_t finfo = 0; if(finfo_str) { - finfo = parse_finfo(finfo_str, NULL); + finfo = parse_finfo_settings(finfo_str, NULL); } int ret;
--- a/dav/scfg.c Sun Mar 17 15:00:48 2019 +0100 +++ b/dav/scfg.c Sun Mar 17 18:11:31 2019 +0100 @@ -343,7 +343,7 @@ tagconfig = parse_tagconfig(node); } else if(xstreq(node->name, "metadata")) { char *error = NULL; - metadata = parse_finfo(value, &error); + metadata = parse_finfo_settings(value, &error); if(error) { print_error(node->line, "unknown metadata: %s\n", error); free(error);
--- a/dav/sync.c Sun Mar 17 15:00:48 2019 +0100 +++ b/dav/sync.c Sun Mar 17 18:11:31 2019 +0100 @@ -33,6 +33,7 @@ #include <unistd.h> #include <signal.h> #include <time.h> +#include <utime.h> #include <libxml/xmlerror.h> #include <sys/types.h> #include <ucx/string.h> @@ -499,7 +500,7 @@ } int ret = 0; - DavResource *ls = dav_query(sn, "select D:getetag,idav:status,idav:tags from / with depth = infinity"); + DavResource *ls = dav_query(sn, "select D:getetag,idav:status,idav:tags,idav:finfo,idav:xattributes from / with depth = infinity"); if(!ls) { print_resource_error(sn, "/"); if(locked) { @@ -549,7 +550,7 @@ UcxList *res_broken = NULL; UcxList *lres_removed = NULL; // list of LocalResource* - UcxMap *svrres = ucx_map_new(db->resources->count); + //UcxMap *svrres = ucx_map_new(db->resources->count); UcxMap *dbres = ucx_map_clone(db->resources, NULL, NULL); UcxList *statls = NULL; @@ -695,9 +696,18 @@ LocalResource *local = ucx_map_cstr_get(db->resources, res->path); if(local) { + printf("update: %s\n", res->path); char *local_path = util_concat_path(dir->path, res->path); - if(sync_store_tags(dir, local_path, local, res)) { - fprintf(stderr, "Tag update failed: %s\n", res->path); + if(sync_store_metadata(dir, local_path, local, res)) { + fprintf(stderr, "Metadata update failed: %s\n", res->path); + sync_error++; + } else { + struct stat s; + if(stat(local_path, &s)) { + fprintf(stderr, "Cannot stat file after update: %s\n", strerror(errno)); + } + sync_set_metadata_from_stat(local, &s); + sync_success++; } free(local_path); } else { @@ -819,9 +829,6 @@ } if(!strcmp(e.ptr, local->etag)) { // resource is already up-to-date on the client - - // TODO: detect metadata update - //sync_store_tags(dir, local_path, local, res); nochange = TRUE; } } @@ -838,18 +845,64 @@ ret = REMOTE_CHANGE_NEW; } - if(ret == REMOTE_NO_CHANGE) { + while(ret == REMOTE_NO_CHANGE && local) { // check if tags have changed if(dir->tagconfig) { + DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_NS, "tags"); + UcxList *remote_tags = NULL; + if(tagsprop) { + remote_tags = parse_dav_xml_taglist(tagsprop); + } + char *remote_hash = create_tags_hash(remote_tags); + if(nullstrcmp(remote_hash, local->remote_tags_hash)) { + ret = REMOTE_CHANGE_METADATA; + } + if(remote_hash) { + free(remote_hash); + } + free_taglist(remote_tags); + if(ret == REMOTE_CHANGE_METADATA) { + break; + } } + // check if extended attributes have changed + if(dir->metadata & FINFO_XATTR == FINFO_XATTR) { + DavXmlNode *xattr = dav_get_property_ns(res, DAV_NS, "xattributes"); + char *xattr_hash = get_xattr_hash(xattr); + if(nullstrcmp(xattr_hash, local->xattr_hash)) { + ret = REMOTE_CHANGE_METADATA; + break; + } + } + + // check if finfo has changed + DavXmlNode *finfo = dav_get_property_ns(res, DAV_NS, "finfo"); + if(dir->metadata & FINFO_MODE == FINFO_MODE) { + FileInfo f; + finfo_get_values(finfo, &f); + if(f.mode_set && f.mode != local->mode) { + ret = REMOTE_CHANGE_METADATA; + break; + } + } + + break; } free(local_path); return ret; } +void sync_set_metadata_from_stat(LocalResource *local, struct stat *s) { + local->last_modified = s->st_mtime; + local->mode = s->st_mode & 07777; + local->uid = s->st_uid; + local->gid = s->st_gid; + local->size = s->st_size; +} + int sync_get_resource( CmdArgs *a, SyncDirectory *dir, @@ -887,6 +940,10 @@ if(ret == 0) { (*counter)++; + + if(sync_store_metadata(dir, tmp_path, local, res)) { + fprintf(stderr, "Cannot store metadata: %s\n", res->path); + } if(dir->trash && dir->backuppull) { move_to_trash(dir, local_path); @@ -918,13 +975,11 @@ if(local->etag) { free(local->etag); } + // set metadata from stat local->etag = strdup(etag); - local->last_modified = s.st_mtime; - local->size = s.st_size; + sync_set_metadata_from_stat(local, &s); local->skipped = FALSE; - - sync_store_tags(dir, tmp_path, local, res); } else { if(sys_unlink(tmp_path)) { fprintf(stderr, "Cannot remove tmp file: %s\n", tmp_path); @@ -1849,6 +1904,11 @@ if(db_res->last_modified == res->last_modified && db_res->size == res->size) { return 0; } + } else { + res->tags_updated = 1; + res->finfo_updated = 1; + res->xattr_updated = 1; + res->metadata_updated = 1; } return 1; } @@ -1954,6 +2014,66 @@ return equal; } +int sync_store_metadata(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res) { + int ret = 0; + + DavXmlNode *fileinfo = dav_get_property_ns(res, DAV_NS, "finfo"); + if(fileinfo) { + FileInfo f; + finfo_get_values(fileinfo, &f); + if(dir->metadata & FINFO_DATE == FINFO_DATE && f.date_set) { + // set mtime + struct utimbuf t; + t.actime = f.last_modified; + t.modtime = f.last_modified; + if(utime(path, &t)) { + fprintf(stderr, "utime failed for file: %s : %s\n", path, strerror(errno)); + ret = 1; + } + } + if(dir->metadata & FINFO_MODE == FINFO_MODE && f.mode_set) { + // set mode + if(chmod(path, f.mode)) { + fprintf(stderr, "chmod failed for file: %s : %s\n", path, strerror(errno)); + ret = 1; + } + } + } + + DavXmlNode *xattr_prop = dav_get_property_ns(res, DAV_NS, "xattributes"); + if(xattr_prop) { + XAttributes *xattr = xml_get_attributes(xattr_prop); + if(xattr) { + if(!sync_store_xattr(dir, path, xattr)) { + if(local->xattr_hash) { + free(local->xattr_hash); + } + local->xattr_hash = xattr->hash; + } + } + } + + if(sync_store_tags(dir, path, local, res)) { + ret = 1; + } + + return ret; +} + +int sync_store_xattr(SyncDirectory *dir, const char *path, XAttributes *xattr) { + for(int i=0;i<xattr->nattr;i++) { + sstr_t value = xattr->values[i]; + if(xattr_set(path, xattr->names[i], value.ptr, value.length)) { + fprintf( + stderr, + "Cannot store xattr '%s' for file: %s\n", + xattr->names[i], + path); + } + } + return 0; +} + int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res) { if(!dir->tagconfig) { return 0; @@ -1973,7 +2093,10 @@ UcxList *local_tags = sync_get_file_tags(dir, local, &tags_changed, NULL); if(tags_changed) { switch(dir->tagconfig->conflict) { - case TAG_NO_CONFLICT: + case TAG_NO_CONFLICT: { + store_tags = TRUE; + break; + } case TAG_KEEP_LOCAL: { store_tags = FALSE; break; @@ -2036,7 +2159,7 @@ int update = 1; if(local) { if(!local->tags_hash || strcmp(data_hash, local->tags_hash)) { - printf("update: %s\n", local->path); + //printf("update: %s\n", local->path); } else { update = 0; } @@ -2058,7 +2181,7 @@ } } else { if(local) { - printf("update: %s\n", local->path); + //printf("update: %s\n", local->path); } ret = xattr_remove(path, dir->tagconfig->xattr_name); }
--- a/dav/sync.h Sun Mar 17 15:00:48 2019 +0100 +++ b/dav/sync.h Sun Mar 17 18:11:31 2019 +0100 @@ -99,6 +99,7 @@ SyncDirectory *dir, SyncDatabase *db); +void sync_set_metadata_from_stat(LocalResource *local, struct stat *s); int sync_get_resource( CmdArgs *a, SyncDirectory *dir, @@ -135,6 +136,8 @@ UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res); UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash); int sync_tags_equal(UcxList *tags1, UcxList *tags2); +int sync_store_metadata(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res); +int sync_store_xattr(SyncDirectory *dir, const char *path, XAttributes *xattr); int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res); int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, UcxList *tags); int sync_put_resource(
--- a/libidav/webdav.h Sun Mar 17 15:00:48 2019 +0100 +++ b/libidav/webdav.h Sun Mar 17 18:11:31 2019 +0100 @@ -342,6 +342,7 @@ DavXmlNode* dav_xml_createtextnode(const char *text); void dav_xml_add_child(DavXmlNode *node, DavXmlNode *child); void dav_xml_add_attr(DavXmlNode *node, const char *name, const char *value); +char* dav_xml_get_attr(DavXmlNode *node, const char *name); DavXmlNode* dav_parse_xml(DavSession *sn, const char *str, size_t len);
--- a/libidav/xml.c Sun Mar 17 15:00:48 2019 +0100 +++ b/libidav/xml.c Sun Mar 17 18:11:31 2019 +0100 @@ -345,6 +345,18 @@ } } +char* dav_xml_get_attr(DavXmlNode *node, const char *name) { + DavXmlAttr *attr = node->attributes; + while(attr) { + if(!strcmp(attr->name, name)) { + return attr->value; + } + + attr = attr->next; + } + return NULL; +} + DavXmlNode* dav_parse_xml(DavSession *sn, const char *str, size_t len) { xmlDoc *doc = xmlReadMemory(str, len, NULL, NULL, 0); if(!doc) {