UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2018 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <errno.h> 33 34 #include "db.h" 35 36 #include <cx/utils.h> 37 38 #include <libidav/utils.h> 39 40 #include <libxml/encoding.h> 41 #include <libxml/xmlwriter.h> 42 43 44 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) 45 46 #ifdef _WIN32 47 #define ENV_HOME getenv("USERPROFILE") 48 #else 49 #define ENV_HOME getenv("HOME") 50 #endif /* _WIN32 */ 51 52 SyncDatabase* load_db(char *name) { 53 char *dav_dir = util_concat_path(ENV_HOME, ".dav"); 54 char *db_file = util_concat_path(dav_dir, name); 55 free(dav_dir); 56 57 SyncDatabase *db = malloc(sizeof(SyncDatabase)); 58 db->resources = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 2048); 59 db->conflict = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 60 61 cxDefineDestructor(db->resources, local_resource_free); 62 cxDefineDestructor(db->conflict, local_resource_free); 63 64 xmlTextReaderPtr reader = xmlReaderForFile(db_file, NULL, 0); 65 if(!reader) { 66 xmlDoc *doc = doc = xmlNewDoc(BAD_CAST "1.0"); 67 xmlNode *root = xmlNewNode(NULL, BAD_CAST "directory"); 68 xmlDocSetRootElement(doc, root); 69 if(xmlSaveFormatFileEnc(db_file, doc, "UTF-8", 1) == -1) { 70 destroy_db(db); 71 db = NULL; 72 } 73 xmlFreeDoc(doc); 74 free(db_file); 75 return db; 76 } 77 free(db_file); 78 79 int error = 0; 80 while(xmlTextReaderRead(reader)) { 81 int type = xmlTextReaderNodeType(reader); 82 const xmlChar *xmlName = xmlTextReaderConstName(reader); 83 84 if(type == XML_READER_TYPE_ELEMENT) { 85 if(xstreq(xmlName, "resource")) { 86 LocalResource *res = process_resource(reader); 87 if(res) { 88 cxMapPut(db->resources, cx_hash_key_str(res->path), res); 89 } else { 90 error = 1; 91 break; 92 } 93 } else if(xstreq(xmlName, "conflict")) { 94 LocalResource *res = process_conflict(reader); 95 if(res) { 96 cxMapPut(db->conflict, cx_hash_key_str(res->path), res); 97 } else { 98 error = 1; 99 break; 100 } 101 } 102 } 103 } 104 105 xmlFreeTextReader(reader); 106 if(error) { 107 destroy_db(db); 108 return NULL; 109 } else { 110 return db; 111 } 112 } 113 114 void process_parts(xmlTextReaderPtr reader, LocalResource *res) { 115 // TODO: rewrite using low level array 116 117 CxList *parts = cxLinkedListCreateSimple(CX_STORE_POINTERS); 118 119 FilePart *current_part = NULL; 120 121 size_t count = 0; 122 int field = -1; 123 int err = 0; 124 while(xmlTextReaderRead(reader)) { 125 int type = xmlTextReaderNodeType(reader); 126 const xmlChar *name = xmlTextReaderConstName(reader); 127 int depth = xmlTextReaderDepth(reader); 128 129 if(type == XML_READER_TYPE_ELEMENT) { 130 if(depth == 3 && xstreq(name, "part")) { 131 current_part = calloc(1, sizeof(FilePart)); 132 current_part->block = count; 133 cxListAdd(parts, current_part); 134 count++; 135 } else if(depth == 4) { 136 if(xstreq(name, "hash")) { 137 field = 0; 138 } else if(xstreq(name, "etag")) { 139 field = 1; 140 } 141 } 142 } else if(type == XML_READER_TYPE_END_ELEMENT) { 143 if(depth == 2) { 144 // </parts> 145 break; 146 } else if(depth == 3) { 147 if(current_part) { 148 if(!current_part->hash || !current_part->etag) { 149 err = 1; 150 } 151 } 152 // </part> 153 current_part = NULL; 154 } 155 field = -1; 156 } else if(type == XML_READER_TYPE_TEXT && depth == 5 && current_part) { 157 const char *text = (const char*)xmlTextReaderConstValue(reader); 158 if(field == 0) { 159 current_part->hash = strdup(text); 160 } else if(field == 1) { 161 current_part->etag = strdup(text); 162 } 163 } 164 } 165 166 if(!err) { 167 FilePart *file_parts = calloc(count, sizeof(FilePart)); 168 CxIterator iter = cxListIterator(parts); 169 cx_foreach(FilePart*, p, iter) { 170 file_parts[iter.index] = *p; 171 free(p); 172 } 173 174 res->parts = file_parts; 175 res->numparts = count; 176 } 177 178 cxListDestroy(parts); 179 } 180 181 LocalResource* process_resource(xmlTextReaderPtr reader) { 182 LocalResource *res = calloc(1, sizeof(LocalResource)); 183 184 int field = -1; 185 while(xmlTextReaderRead(reader)) { 186 int type = xmlTextReaderNodeType(reader); 187 const xmlChar *name = xmlTextReaderConstName(reader); 188 189 if(type == XML_READER_TYPE_ELEMENT) { 190 if(xstreq(name, "path")) { 191 field = 0; 192 } else if(xstreq(name, "etag")) { 193 field = 1; 194 } else if(xstreq(name, "lastmodified")) { 195 field = 2; 196 } else if(xstreq(name, "size")) { 197 field = 3; 198 } else if(xstreq(name, "tags-hash")) { 199 field = 4; 200 } else if(xstreq(name, "mode")) { 201 field = 5; 202 } else if(xstreq(name, "uid")) { 203 field = 6; 204 } else if(xstreq(name, "gid")) { 205 field = 7; 206 } else if(xstreq(name, "xattr-hash")) { 207 field = 8; 208 } else if(xstreq(name, "remote-tags-hash")) { 209 field = 9; 210 } else if(xstreq(name, "blocksize")) { 211 field = 10; 212 } else if(xstreq(name, "hash")) { 213 field = 11; 214 } else if(xstreq(name, "link")) { 215 field = 12; 216 } else if(xstreq(name, "localpath")) { 217 field = 13; 218 } else if(xstreq(name, "versioncontrol")) { 219 field = 14; 220 } else if(xstreq(name, "skipped")) { 221 res->skipped = TRUE; 222 } else if(xstreq(name, "tags-updated")) { 223 res->tags_updated = TRUE; 224 } else if(xstreq(name, "parts")) { 225 process_parts(reader, res); 226 } else if(xstreq(name, "isdirectory")) { 227 res->isdirectory = 1; 228 } 229 } else if(type == XML_READER_TYPE_TEXT) { 230 const xmlChar *value = xmlTextReaderConstValue(reader); 231 //int b = 0; 232 switch(field) { 233 case 0: { 234 res->path = strdup((char*)value); 235 break; 236 } 237 case 1: { 238 res->etag = strdup((char*)value); 239 break; 240 } 241 case 2: { 242 //res->last_modified = util_parse_lastmodified((char*)value); 243 //res->last_modified = atoi((char*)value); 244 char *endptr = (char*)value; 245 time_t t = strtoll((char*)value, &endptr, 10); 246 if(endptr == (char*)value) { 247 fprintf( 248 stderr, 249 "lastmodified does not contain a number: %s\n", value); 250 } else { 251 res->last_modified = t; 252 } 253 break; 254 } 255 case 3: { 256 res->size = 0; 257 int64_t filelen = 0; 258 if(util_strtoint((char*)value, &filelen)) { 259 if(filelen > 0) { 260 res->size = (size_t)filelen; 261 } 262 } 263 break; 264 } 265 case 4: { 266 res->tags_hash = strdup((char*)value); 267 break; 268 } 269 case 5: { 270 char *end; 271 errno = 0; 272 long int mode = strtol((char*)value, &end, 8); 273 if(errno == 0) { 274 res->mode = (mode_t)mode; 275 } 276 break; 277 } 278 case 6: { 279 uint64_t uid = 0; 280 if(util_strtouint((char*)value, &uid)) { 281 res->uid = (uid_t)uid; 282 } 283 break; 284 } 285 case 7: { 286 uint64_t gid = 0; 287 if(util_strtouint((char*)value, &gid)) { 288 res->gid = (gid_t)gid; 289 } 290 break; 291 } 292 case 8: { 293 res->xattr_hash = strdup((char*)value); 294 break; 295 } 296 case 9: { 297 res->remote_tags_hash = strdup((char*)value); 298 break; 299 } 300 case 10: { 301 int64_t blsz = 0; 302 if(util_strtoint((char*)value, &blsz)) { 303 if(blsz < -1) { 304 blsz = -1; 305 } 306 if(blsz > 0 && blsz < 16) { 307 blsz = 0; 308 } 309 res->blocksize = blsz; 310 } 311 break; 312 } 313 case 11: { 314 res->hash = strdup((char*)value); 315 break; 316 } 317 case 12: { 318 res->link_target = strdup((char*)value); 319 break; 320 } 321 case 13: { 322 res->local_path = strdup((char*)value); 323 } 324 case 14: { 325 res->versioncontrol = util_getboolean((char*)value); 326 } 327 } 328 } else if(XML_READER_TYPE_END_ELEMENT) { 329 if(xstreq(name, "resource")) { 330 break; 331 } else { 332 field = -1; 333 } 334 } 335 } 336 337 if(!res->path) { 338 // TODO: free res 339 return NULL; 340 } else { 341 return res; 342 } 343 } 344 345 LocalResource* process_conflict(xmlTextReaderPtr reader) { 346 LocalResource *res = calloc(1, sizeof(LocalResource)); 347 348 int field = 0; 349 while(xmlTextReaderRead(reader)) { 350 int type = xmlTextReaderNodeType(reader); 351 const xmlChar *name = xmlTextReaderConstName(reader); 352 353 if(type == XML_READER_TYPE_ELEMENT) { 354 if(xstreq(name, "path")) { 355 field = 1; 356 } else if(xstreq(name, "source")) { 357 field = 2; 358 } 359 } else if(type == XML_READER_TYPE_TEXT) { 360 const xmlChar *value = xmlTextReaderConstValue(reader); 361 switch(field) { 362 case 1: { 363 res->path = strdup((const char*)value); 364 break; 365 } 366 case 2: { 367 res->conflict_source = strdup((const char*)value); 368 break; 369 } 370 } 371 } else if(XML_READER_TYPE_END_ELEMENT) { 372 if(xstreq(name, "conflict")) { 373 break; 374 } else { 375 field = 0; 376 } 377 } 378 } 379 380 if(!res->path) { 381 // TODO: free res 382 return NULL; 383 } else { 384 return res; 385 } 386 } 387 388 int store_db(SyncDatabase *db, char *name, uint32_t settings) { 389 // open writer 390 char *dav_dir = util_concat_path(ENV_HOME, ".dav"); 391 char *db_file = util_concat_path(dav_dir, name); 392 free(dav_dir); 393 xmlTextWriterPtr writer = xmlNewTextWriterFilename(db_file, 0); 394 if(!writer) { 395 fprintf(stderr, "Cannot write db file: %s\n", db_file); 396 free(db_file); 397 return -1; 398 } 399 free(db_file); 400 401 // start document 402 int r = 0; 403 r = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL); 404 if(r < 0) { 405 xmlFreeTextWriter(writer); 406 return -1; 407 } 408 xmlTextWriterStartElement(writer, BAD_CAST "directory"); 409 410 // write all resource entries 411 CxIterator iter = cxMapIteratorValues(db->resources); 412 cx_foreach(LocalResource*, res, iter) { 413 // <resource> 414 xmlTextWriterStartElement(writer, BAD_CAST "resource"); 415 416 r = xmlTextWriterWriteElement( 417 writer, 418 BAD_CAST "path", 419 BAD_CAST res->path); 420 if(r < 0) { 421 fprintf(stderr, "Cannot write path: %s\n", res->path); 422 xmlFreeTextWriter(writer); 423 return -1; 424 } 425 426 if(res->isdirectory) { 427 r = xmlTextWriterStartElement(writer, BAD_CAST "isdirectory"); 428 r += xmlTextWriterEndElement(writer); 429 if(r < 0) { 430 fprintf(stderr, "Cannot write isdirectory\n"); 431 xmlFreeTextWriter(writer); 432 return -1; 433 } 434 } 435 436 if(res->etag) { 437 r = xmlTextWriterWriteElement( 438 writer, 439 BAD_CAST "etag", 440 BAD_CAST res->etag); 441 if(r < 0) { 442 fprintf(stderr, "Cannot write etag: %s\n", res->etag); 443 xmlFreeTextWriter(writer); 444 return -1; 445 } 446 } 447 448 if(res->hash) { 449 r = xmlTextWriterWriteElement( 450 writer, 451 BAD_CAST "hash", 452 BAD_CAST res->hash); 453 if(r < 0) { 454 fprintf(stderr, "Cannot write hash: %s\n", res->hash); 455 xmlFreeTextWriter(writer); 456 return -1; 457 } 458 } 459 460 r = xmlTextWriterWriteFormatElement( 461 writer, 462 BAD_CAST "lastmodified", 463 "%" PRId64, 464 (int64_t)res->last_modified); 465 if(r < 0) { 466 fprintf(stderr, "Cannot write lastmodified\n"); 467 xmlFreeTextWriter(writer); 468 return -1; 469 } 470 471 if(res->blocksize != 0) { 472 r = xmlTextWriterWriteFormatElement( 473 writer, 474 BAD_CAST "mode", 475 "%" PRId64, 476 res->blocksize); 477 if(r < 0) { 478 fprintf(stderr, "Cannot write blocksize\n"); 479 xmlFreeTextWriter(writer); 480 return -1; 481 } 482 } 483 484 if((settings & DB_STORE_MODE) == DB_STORE_MODE) { 485 r = xmlTextWriterWriteFormatElement( 486 writer, 487 BAD_CAST "mode", 488 "%o", 489 (int)res->mode); 490 if(r < 0) { 491 fprintf(stderr, "Cannot write mode\n"); 492 xmlFreeTextWriter(writer); 493 return -1; 494 } 495 } 496 497 if((settings & DB_STORE_OWNER) == DB_STORE_OWNER) { 498 r = xmlTextWriterWriteFormatElement( 499 writer, 500 BAD_CAST "uid", 501 "%u", 502 (unsigned int)res->uid); 503 if(r < 0) { 504 fprintf(stderr, "Cannot write uid\n"); 505 xmlFreeTextWriter(writer); 506 return -1; 507 } 508 r = xmlTextWriterWriteFormatElement( 509 writer, 510 BAD_CAST "gid", 511 "%u", 512 (unsigned int)res->gid); 513 if(r < 0) { 514 fprintf(stderr, "Cannot write gid\n"); 515 xmlFreeTextWriter(writer); 516 return -1; 517 } 518 } 519 520 r = xmlTextWriterWriteFormatElement( 521 writer, 522 BAD_CAST "size", 523 "%" PRId64, 524 (int64_t)res->size); 525 if(r < 0) { 526 fprintf(stderr, "Cannot write size\n"); 527 xmlFreeTextWriter(writer); 528 return -1; 529 } 530 531 if(res->tags_hash) { 532 r = xmlTextWriterWriteElement( 533 writer, 534 BAD_CAST "tags-hash", 535 BAD_CAST res->tags_hash); 536 if(r < 0) { 537 fprintf(stderr, "Cannot write tags-hash: %s\n", res->tags_hash); 538 xmlFreeTextWriter(writer); 539 return -1; 540 } 541 } 542 543 if(res->remote_tags_hash) { 544 r = xmlTextWriterWriteElement( 545 writer, 546 BAD_CAST "remote-tags-hash", 547 BAD_CAST res->remote_tags_hash); 548 if(r < 0) { 549 fprintf(stderr, "Cannot write remote-tags-hash: %s\n", res->remote_tags_hash); 550 xmlFreeTextWriter(writer); 551 return -1; 552 } 553 } 554 555 if(res->link_target) { 556 r = xmlTextWriterWriteElement( 557 writer, 558 BAD_CAST "link", 559 BAD_CAST res->link_target); 560 if(r < 0) { 561 fprintf(stderr, "Cannot write link: %s\n", res->link_target); 562 xmlFreeTextWriter(writer); 563 return -1; 564 } 565 } 566 567 if(res->local_path) { 568 r = xmlTextWriterWriteElement( 569 writer, 570 BAD_CAST "localpath", 571 BAD_CAST res->local_path); 572 if(r < 0) { 573 fprintf(stderr, "Cannot write localpath: %s\n", res->local_path); 574 xmlFreeTextWriter(writer); 575 return -1; 576 } 577 } 578 579 580 if(res->xattr_hash) { 581 r = xmlTextWriterWriteElement( 582 writer, 583 BAD_CAST "xattr-hash", 584 BAD_CAST res->xattr_hash); 585 if(r < 0) { 586 fprintf(stderr, "Cannot write xattr-hash: %s\n", res->xattr_hash); 587 xmlFreeTextWriter(writer); 588 return -1; 589 } 590 } 591 592 if(res->skipped) { 593 r = xmlTextWriterStartElement(writer, BAD_CAST "skipped"); 594 r += xmlTextWriterEndElement(writer); 595 if(r < 0) { 596 fprintf(stderr, "Cannot write skipped\n"); 597 xmlFreeTextWriter(writer); 598 return -1; 599 } 600 } 601 602 if(res->tags_updated) { 603 r = xmlTextWriterStartElement(writer, BAD_CAST "tags-updated"); 604 r += xmlTextWriterEndElement(writer); 605 if(r < 0) { 606 fprintf(stderr, "Cannot write tags-updated\n"); 607 xmlFreeTextWriter(writer); 608 return -1; 609 } 610 } 611 612 if(res->numparts > 0) { 613 r = xmlTextWriterStartElement(writer, BAD_CAST "parts"); 614 if(r < 0) { 615 xmlFreeTextWriter(writer); 616 return -1; 617 } 618 for(size_t i=0;i<res->numparts;i++) { 619 FilePart p = res->parts[i]; 620 r = xmlTextWriterStartElement(writer, BAD_CAST "part"); 621 if(r < 0) { 622 xmlFreeTextWriter(writer); 623 return -1; 624 } 625 626 if(p.hash) { 627 r = xmlTextWriterWriteElement(writer, BAD_CAST "hash", BAD_CAST p.hash); 628 if(r < 0) { 629 xmlFreeTextWriter(writer); 630 return -1; 631 } 632 } 633 if(p.etag) { 634 r = xmlTextWriterWriteElement(writer, BAD_CAST "etag", BAD_CAST p.etag); 635 if(r < 0) { 636 xmlFreeTextWriter(writer); 637 return -1; 638 } 639 } 640 r = xmlTextWriterEndElement(writer); 641 if(r < 0) { 642 xmlFreeTextWriter(writer); 643 return -1; 644 } 645 } 646 r = xmlTextWriterEndElement(writer); 647 if(r < 0) { 648 xmlFreeTextWriter(writer); 649 return -1; 650 } 651 } 652 653 if(res->versioncontrol) { 654 r = xmlTextWriterWriteElement( 655 writer, 656 BAD_CAST "versioncontrol", 657 BAD_CAST "true"); 658 if(r < 0) { 659 fprintf(stderr, "Cannot write versioncontrol\n"); 660 xmlFreeTextWriter(writer); 661 return -1; 662 } 663 } 664 665 // </resource> 666 xmlTextWriterEndElement(writer); 667 } 668 669 // write all remove entries 670 /* 671 i = ucx_map_iterator(db->remove); 672 UCX_MAP_FOREACH(key, res, i) { 673 // <remove> 674 xmlTextWriterStartElement(writer, BAD_CAST "remove"); 675 676 xmlTextWriterWriteElement( 677 writer, 678 BAD_CAST "path", 679 BAD_CAST res->path); 680 681 // </remove> 682 xmlTextWriterEndElement(writer); 683 } 684 */ 685 686 // write all conflict entries 687 iter = cxMapIteratorValues(db->conflict); 688 cx_foreach(LocalResource*, res, iter) { 689 // <conflict> 690 xmlTextWriterStartElement(writer, BAD_CAST "conflict"); 691 692 xmlTextWriterWriteElement( 693 writer, 694 BAD_CAST "path", 695 BAD_CAST res->path); 696 697 if(res->conflict_source) { 698 xmlTextWriterWriteElement( 699 writer, 700 BAD_CAST "source", 701 BAD_CAST res->conflict_source); 702 } 703 704 // </conflict> 705 xmlTextWriterEndElement(writer); 706 } 707 708 // end 709 xmlTextWriterEndElement(writer); 710 r = xmlTextWriterEndDocument(writer); 711 if(r < 0) { 712 xmlFreeTextWriter(writer); 713 return -1; 714 } 715 xmlFreeTextWriter(writer); 716 return 0; 717 } 718 719 void destroy_db(SyncDatabase *db) { 720 cxMapDestroy(db->resources); 721 cxMapDestroy(db->conflict); 722 free(db); 723 } 724 725 void local_resource_free(LocalResource *res) { 726 if(!res) { 727 return; 728 } 729 if(res->name) { 730 free(res->name); 731 } 732 if(res->path) { 733 free(res->path); 734 } 735 if(res->etag) { 736 free(res->etag); 737 } 738 if(res->cached_tags) { 739 cxBufferFree(res->cached_tags); 740 } 741 if(res->tags_hash) { 742 free(res->tags_hash); 743 } 744 if(res->prev_hash) { 745 free(res->prev_hash); 746 } 747 free(res); 748 } 749 750 static char* nullstrdup(const char *s) { 751 return s ? strdup(s) : NULL; 752 } 753 754 void local_resource_copy_parts(LocalResource *from, LocalResource *to) { 755 if(from->parts) { 756 to->numparts = from->numparts; 757 to->parts = calloc(from->numparts, sizeof(FilePart)); 758 for(int i=0;i<to->numparts;i++) { 759 FilePart s = from->parts[i]; 760 FilePart p; 761 p.block = s.block; 762 p.hash = nullstrdup(s.hash); 763 p.etag = nullstrdup(s.etag); 764 to->parts[i] = p; 765 } 766 } 767 } 768 769 LocalResource* local_resource_copy(LocalResource *src, const char *new_path) { 770 LocalResource *newres = calloc(1, sizeof(LocalResource)); 771 newres->path = strdup(new_path); 772 newres->etag = nullstrdup(src->etag); 773 newres->hash = nullstrdup(src->hash); 774 newres->last_modified = src->last_modified; 775 newres->mode = src->mode; 776 newres->uid = src->uid; 777 newres->gid = src->gid; 778 newres->size = src->size; 779 newres->isdirectory = src->isdirectory; 780 newres->skipped = src->skipped; 781 newres->versioncontrol = src->versioncontrol; 782 783 if(src->xattr) { 784 XAttributes *xattr = calloc(1, sizeof(XAttributes)); 785 xattr->hash = nullstrdup(src->xattr->hash); 786 xattr->nattr = src->xattr->nattr; 787 xattr->names = calloc(xattr->nattr, sizeof(char*)); 788 xattr->values = calloc(xattr->nattr, sizeof(cxmutstr)); 789 for(int i=0;i<xattr->nattr;i++) { 790 xattr->names[i] = strdup(src->xattr->names[i]); 791 xattr->values[i] = cx_strdup(cx_strcast(src->xattr->values[i])); 792 } 793 newres->xattr = xattr; 794 } 795 796 newres->tags_hash = nullstrdup(src->tags_hash); 797 newres->xattr_hash = nullstrdup(src->xattr_hash); 798 newres->remote_tags_hash = nullstrdup(src->remote_tags_hash); 799 800 local_resource_copy_parts(src, newres); 801 802 newres->blocksize = src->blocksize; 803 804 newres->tags_updated = src->tags_updated; 805 newres->finfo_updated = src->finfo_updated; 806 newres->xattr_updated = src->xattr_updated; 807 newres->metadata_updated = src->metadata_updated; 808 809 return newres; 810 811 } 812 813 void filepart_free(FilePart *part) { 814 if(part->etag) { 815 free(part->etag); 816 } 817 if(part->hash) { 818 free(part->hash); 819 } 820 free(part); 821 } 822 823 CxMap* create_hash_index(SyncDatabase *db) { 824 CxMap *hmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, cxMapSize(db->resources) + 64); 825 826 CxIterator i = cxMapIteratorValues(db->resources); 827 cx_foreach(LocalResource*, res, i) { 828 if(res->hash) { 829 cxMapPut(hmap, cx_hash_key_str(res->hash), res); 830 } 831 } 832 833 return hmap; 834 } 835