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 db->resources->destructor_data = (cx_destructor_func)local_resource_free; 62 db->conflict->destructor_data = (cx_destructor_func)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 *name = xmlTextReaderConstName(reader); 83 84 if(type == XML_READER_TYPE_ELEMENT) { 85 if(xstreq(name, "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(name, "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 parts->destructor_data = (cx_destructor_func)filepart_free; 119 120 FilePart *current_part = NULL; 121 122 size_t count = 0; 123 int field = -1; 124 int err = 0; 125 while(xmlTextReaderRead(reader)) { 126 int type = xmlTextReaderNodeType(reader); 127 const xmlChar *name = xmlTextReaderConstName(reader); 128 int depth = xmlTextReaderDepth(reader); 129 130 if(type == XML_READER_TYPE_ELEMENT) { 131 if(depth == 3 && xstreq(name, "part")) { 132 current_part = calloc(1, sizeof(FilePart)); 133 current_part->block = count; 134 cxListAdd(parts, current_part); 135 count++; 136 } else if(depth == 4) { 137 if(xstreq(name, "hash")) { 138 field = 0; 139 } else if(xstreq(name, "etag")) { 140 field = 1; 141 } 142 } 143 } else if(type == XML_READER_TYPE_END_ELEMENT) { 144 if(depth == 2) { 145 // </parts> 146 break; 147 } else if(depth == 3) { 148 if(current_part) { 149 if(!current_part->hash || !current_part->etag) { 150 err = 1; 151 } 152 } 153 // </part> 154 current_part = NULL; 155 } 156 field = -1; 157 } else if(type == XML_READER_TYPE_TEXT && depth == 5 && current_part) { 158 const char *text = (const char*)xmlTextReaderConstValue(reader); 159 if(field == 0) { 160 current_part->hash = strdup(text); 161 } else if(field == 1) { 162 current_part->etag = strdup(text); 163 } 164 } 165 } 166 167 if(!err) { 168 FilePart *file_parts = calloc(count, sizeof(FilePart)); 169 size_t i = 0; 170 CxIterator iter = cxListIterator(parts); 171 cx_foreach(FilePart*, p, iter) { 172 file_parts[i] = *p; 173 free(p); 174 i++; 175 } 176 177 res->parts = file_parts; 178 res->numparts = count; 179 } 180 181 cxListDestroy(parts); 182 } 183 184 LocalResource* process_resource(xmlTextReaderPtr reader) { 185 LocalResource *res = calloc(1, sizeof(LocalResource)); 186 187 int field = -1; 188 while(xmlTextReaderRead(reader)) { 189 int type = xmlTextReaderNodeType(reader); 190 const xmlChar *name = xmlTextReaderConstName(reader); 191 192 if(type == XML_READER_TYPE_ELEMENT) { 193 if(xstreq(name, "path")) { 194 field = 0; 195 } else if(xstreq(name, "etag")) { 196 field = 1; 197 } else if(xstreq(name, "lastmodified")) { 198 field = 2; 199 } else if(xstreq(name, "size")) { 200 field = 3; 201 } else if(xstreq(name, "tags-hash")) { 202 field = 4; 203 } else if(xstreq(name, "mode")) { 204 field = 5; 205 } else if(xstreq(name, "uid")) { 206 field = 6; 207 } else if(xstreq(name, "gid")) { 208 field = 7; 209 } else if(xstreq(name, "xattr-hash")) { 210 field = 8; 211 } else if(xstreq(name, "remote-tags-hash")) { 212 field = 9; 213 } else if(xstreq(name, "blocksize")) { 214 field = 10; 215 } else if(xstreq(name, "hash")) { 216 field = 11; 217 } else if(xstreq(name, "link")) { 218 field = 12; 219 } else if(xstreq(name, "localpath")) { 220 field = 13; 221 } else if(xstreq(name, "versioncontrol")) { 222 field = 14; 223 } else if(xstreq(name, "skipped")) { 224 res->skipped = TRUE; 225 } else if(xstreq(name, "tags-updated")) { 226 res->tags_updated = TRUE; 227 } else if(xstreq(name, "parts")) { 228 process_parts(reader, res); 229 } else if(xstreq(name, "isdirectory")) { 230 res->isdirectory = 1; 231 } 232 } else if(type == XML_READER_TYPE_TEXT) { 233 const xmlChar *value = xmlTextReaderConstValue(reader); 234 //int b = 0; 235 switch(field) { 236 case 0: { 237 res->path = strdup((char*)value); 238 break; 239 } 240 case 1: { 241 res->etag = strdup((char*)value); 242 break; 243 } 244 case 2: { 245 //res->last_modified = util_parse_lastmodified((char*)value); 246 //res->last_modified = atoi((char*)value); 247 char *endptr = (char*)value; 248 time_t t = strtoll((char*)value, &endptr, 10); 249 if(endptr == (char*)value) { 250 fprintf( 251 stderr, 252 "lastmodified does not contain a number: %s\n", value); 253 } else { 254 res->last_modified = t; 255 } 256 break; 257 } 258 case 3: { 259 res->size = 0; 260 int64_t filelen = 0; 261 if(util_strtoint((char*)value, &filelen)) { 262 if(filelen > 0) { 263 res->size = (size_t)filelen; 264 } 265 } 266 break; 267 } 268 case 4: { 269 res->tags_hash = strdup((char*)value); 270 break; 271 } 272 case 5: { 273 char *end; 274 errno = 0; 275 long int mode = strtol((char*)value, &end, 8); 276 if(errno == 0) { 277 res->mode = (mode_t)mode; 278 } 279 break; 280 } 281 case 6: { 282 uint64_t uid = 0; 283 if(util_strtouint((char*)value, &uid)) { 284 res->uid = (uid_t)uid; 285 } 286 break; 287 } 288 case 7: { 289 uint64_t gid = 0; 290 if(util_strtouint((char*)value, &gid)) { 291 res->gid = (gid_t)gid; 292 } 293 break; 294 } 295 case 8: { 296 res->xattr_hash = strdup((char*)value); 297 break; 298 } 299 case 9: { 300 res->remote_tags_hash = strdup((char*)value); 301 break; 302 } 303 case 10: { 304 int64_t blsz = 0; 305 if(util_strtoint((char*)value, &blsz)) { 306 if(blsz < -1) { 307 blsz = -1; 308 } 309 if(blsz > 0 && blsz < 16) { 310 blsz = 0; 311 } 312 res->blocksize = blsz; 313 } 314 break; 315 } 316 case 11: { 317 res->hash = strdup((char*)value); 318 break; 319 } 320 case 12: { 321 res->link_target = strdup((char*)value); 322 break; 323 } 324 case 13: { 325 res->local_path = strdup((char*)value); 326 } 327 case 14: { 328 res->versioncontrol = util_getboolean((char*)value); 329 } 330 } 331 } else if(XML_READER_TYPE_END_ELEMENT) { 332 if(xstreq(name, "resource")) { 333 break; 334 } else { 335 field = -1; 336 } 337 } 338 } 339 340 if(!res->path) { 341 // TODO: free res 342 return NULL; 343 } else { 344 return res; 345 } 346 } 347 348 LocalResource* process_conflict(xmlTextReaderPtr reader) { 349 LocalResource *res = calloc(1, sizeof(LocalResource)); 350 351 int field = 0; 352 while(xmlTextReaderRead(reader)) { 353 int type = xmlTextReaderNodeType(reader); 354 const xmlChar *name = xmlTextReaderConstName(reader); 355 356 if(type == XML_READER_TYPE_ELEMENT) { 357 if(xstreq(name, "path")) { 358 field = 1; 359 } else if(xstreq(name, "source")) { 360 field = 2; 361 } 362 } else if(type == XML_READER_TYPE_TEXT) { 363 const xmlChar *value = xmlTextReaderConstValue(reader); 364 switch(field) { 365 case 1: { 366 res->path = strdup((const char*)value); 367 break; 368 } 369 case 2: { 370 res->conflict_source = strdup((const char*)value); 371 break; 372 } 373 } 374 } else if(XML_READER_TYPE_END_ELEMENT) { 375 if(xstreq(name, "conflict")) { 376 break; 377 } else { 378 field = 0; 379 } 380 } 381 } 382 383 if(!res->path) { 384 // TODO: free res 385 return NULL; 386 } else { 387 return res; 388 } 389 } 390 391 int store_db(SyncDatabase *db, char *name, uint32_t settings) { 392 // open writer 393 char *dav_dir = util_concat_path(ENV_HOME, ".dav"); 394 char *db_file = util_concat_path(dav_dir, name); 395 free(dav_dir); 396 xmlTextWriterPtr writer = xmlNewTextWriterFilename(db_file, 0); 397 if(!writer) { 398 fprintf(stderr, "Cannot write db file: %s\n", db_file); 399 free(db_file); 400 return -1; 401 } 402 free(db_file); 403 404 // start document 405 int r = 0; 406 r = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL); 407 if(r < 0) { 408 xmlFreeTextWriter(writer); 409 return -1; 410 } 411 xmlTextWriterStartElement(writer, BAD_CAST "directory"); 412 413 // write all resource entries 414 CxIterator i = cxMapIteratorValues(db->resources); 415 LocalResource *res; 416 cx_foreach(LocalResource*, res, i) { 417 // <resource> 418 xmlTextWriterStartElement(writer, BAD_CAST "resource"); 419 420 r = xmlTextWriterWriteElement( 421 writer, 422 BAD_CAST "path", 423 BAD_CAST res->path); 424 if(r < 0) { 425 fprintf(stderr, "Cannot write path: %s\n", res->path); 426 xmlFreeTextWriter(writer); 427 return -1; 428 } 429 430 if(res->isdirectory) { 431 r = xmlTextWriterStartElement(writer, BAD_CAST "isdirectory"); 432 r += xmlTextWriterEndElement(writer); 433 if(r < 0) { 434 fprintf(stderr, "Cannot write isdirectory\n"); 435 xmlFreeTextWriter(writer); 436 return -1; 437 } 438 } 439 440 if(res->etag) { 441 r = xmlTextWriterWriteElement( 442 writer, 443 BAD_CAST "etag", 444 BAD_CAST res->etag); 445 if(r < 0) { 446 fprintf(stderr, "Cannot write etag: %s\n", res->etag); 447 xmlFreeTextWriter(writer); 448 return -1; 449 } 450 } 451 452 if(res->hash) { 453 r = xmlTextWriterWriteElement( 454 writer, 455 BAD_CAST "hash", 456 BAD_CAST res->hash); 457 if(r < 0) { 458 fprintf(stderr, "Cannot write hash: %s\n", res->hash); 459 xmlFreeTextWriter(writer); 460 return -1; 461 } 462 } 463 464 r = xmlTextWriterWriteFormatElement( 465 writer, 466 BAD_CAST "lastmodified", 467 "%" PRId64, 468 (int64_t)res->last_modified); 469 if(r < 0) { 470 fprintf(stderr, "Cannot write lastmodified\n"); 471 xmlFreeTextWriter(writer); 472 return -1; 473 } 474 475 if(res->blocksize != 0) { 476 r = xmlTextWriterWriteFormatElement( 477 writer, 478 BAD_CAST "mode", 479 "%" PRId64, 480 res->blocksize); 481 if(r < 0) { 482 fprintf(stderr, "Cannot write blocksize\n"); 483 xmlFreeTextWriter(writer); 484 return -1; 485 } 486 } 487 488 if((settings & DB_STORE_MODE) == DB_STORE_MODE) { 489 r = xmlTextWriterWriteFormatElement( 490 writer, 491 BAD_CAST "mode", 492 "%o", 493 (int)res->mode); 494 if(r < 0) { 495 fprintf(stderr, "Cannot write mode\n"); 496 xmlFreeTextWriter(writer); 497 return -1; 498 } 499 } 500 501 if((settings & DB_STORE_OWNER) == DB_STORE_OWNER) { 502 r = xmlTextWriterWriteFormatElement( 503 writer, 504 BAD_CAST "uid", 505 "%u", 506 (unsigned int)res->uid); 507 if(r < 0) { 508 fprintf(stderr, "Cannot write uid\n"); 509 xmlFreeTextWriter(writer); 510 return -1; 511 } 512 r = xmlTextWriterWriteFormatElement( 513 writer, 514 BAD_CAST "gid", 515 "%u", 516 (unsigned int)res->gid); 517 if(r < 0) { 518 fprintf(stderr, "Cannot write gid\n"); 519 xmlFreeTextWriter(writer); 520 return -1; 521 } 522 } 523 524 r = xmlTextWriterWriteFormatElement( 525 writer, 526 BAD_CAST "size", 527 "%" PRId64, 528 (int64_t)res->size); 529 if(r < 0) { 530 fprintf(stderr, "Cannot write size\n"); 531 xmlFreeTextWriter(writer); 532 return -1; 533 } 534 535 if(res->tags_hash) { 536 r = xmlTextWriterWriteElement( 537 writer, 538 BAD_CAST "tags-hash", 539 BAD_CAST res->tags_hash); 540 if(r < 0) { 541 fprintf(stderr, "Cannot write tags-hash: %s\n", res->tags_hash); 542 xmlFreeTextWriter(writer); 543 return -1; 544 } 545 } 546 547 if(res->remote_tags_hash) { 548 r = xmlTextWriterWriteElement( 549 writer, 550 BAD_CAST "remote-tags-hash", 551 BAD_CAST res->remote_tags_hash); 552 if(r < 0) { 553 fprintf(stderr, "Cannot write remote-tags-hash: %s\n", res->remote_tags_hash); 554 xmlFreeTextWriter(writer); 555 return -1; 556 } 557 } 558 559 if(res->link_target) { 560 r = xmlTextWriterWriteElement( 561 writer, 562 BAD_CAST "link", 563 BAD_CAST res->link_target); 564 if(r < 0) { 565 fprintf(stderr, "Cannot write link: %s\n", res->link_target); 566 xmlFreeTextWriter(writer); 567 return -1; 568 } 569 } 570 571 if(res->local_path) { 572 r = xmlTextWriterWriteElement( 573 writer, 574 BAD_CAST "localpath", 575 BAD_CAST res->local_path); 576 if(r < 0) { 577 fprintf(stderr, "Cannot write localpath: %s\n", res->local_path); 578 xmlFreeTextWriter(writer); 579 return -1; 580 } 581 } 582 583 584 if(res->xattr_hash) { 585 r = xmlTextWriterWriteElement( 586 writer, 587 BAD_CAST "xattr-hash", 588 BAD_CAST res->xattr_hash); 589 if(r < 0) { 590 fprintf(stderr, "Cannot write xattr-hash: %s\n", res->xattr_hash); 591 xmlFreeTextWriter(writer); 592 return -1; 593 } 594 } 595 596 if(res->skipped) { 597 r = xmlTextWriterStartElement(writer, BAD_CAST "skipped"); 598 r += xmlTextWriterEndElement(writer); 599 if(r < 0) { 600 fprintf(stderr, "Cannot write skipped\n"); 601 xmlFreeTextWriter(writer); 602 return -1; 603 } 604 } 605 606 if(res->tags_updated) { 607 r = xmlTextWriterStartElement(writer, BAD_CAST "tags-updated"); 608 r += xmlTextWriterEndElement(writer); 609 if(r < 0) { 610 fprintf(stderr, "Cannot write tags-updated\n"); 611 xmlFreeTextWriter(writer); 612 return -1; 613 } 614 } 615 616 if(res->numparts > 0) { 617 r = xmlTextWriterStartElement(writer, BAD_CAST "parts"); 618 if(r < 0) { 619 xmlFreeTextWriter(writer); 620 return -1; 621 } 622 for(size_t i=0;i<res->numparts;i++) { 623 FilePart p = res->parts[i]; 624 r = xmlTextWriterStartElement(writer, BAD_CAST "part"); 625 if(r < 0) { 626 xmlFreeTextWriter(writer); 627 return -1; 628 } 629 630 if(p.hash) { 631 r = xmlTextWriterWriteElement(writer, BAD_CAST "hash", BAD_CAST p.hash); 632 if(r < 0) { 633 xmlFreeTextWriter(writer); 634 return -1; 635 } 636 } 637 if(p.etag) { 638 r = xmlTextWriterWriteElement(writer, BAD_CAST "etag", BAD_CAST p.etag); 639 if(r < 0) { 640 xmlFreeTextWriter(writer); 641 return -1; 642 } 643 } 644 r = xmlTextWriterEndElement(writer); 645 if(r < 0) { 646 xmlFreeTextWriter(writer); 647 return -1; 648 } 649 } 650 r = xmlTextWriterEndElement(writer); 651 if(r < 0) { 652 xmlFreeTextWriter(writer); 653 return -1; 654 } 655 } 656 657 if(res->versioncontrol) { 658 r = xmlTextWriterWriteElement( 659 writer, 660 BAD_CAST "versioncontrol", 661 BAD_CAST "true"); 662 if(r < 0) { 663 fprintf(stderr, "Cannot write versioncontrol\n"); 664 xmlFreeTextWriter(writer); 665 return -1; 666 } 667 } 668 669 // </resource> 670 xmlTextWriterEndElement(writer); 671 } 672 673 // write all remove entries 674 /* 675 i = ucx_map_iterator(db->remove); 676 UCX_MAP_FOREACH(key, res, i) { 677 // <remove> 678 xmlTextWriterStartElement(writer, BAD_CAST "remove"); 679 680 xmlTextWriterWriteElement( 681 writer, 682 BAD_CAST "path", 683 BAD_CAST res->path); 684 685 // </remove> 686 xmlTextWriterEndElement(writer); 687 } 688 */ 689 690 // write all conflict entries 691 i = cxMapIteratorValues(db->conflict); 692 cx_foreach(LocalResource*, res, i) { 693 // <conflict> 694 xmlTextWriterStartElement(writer, BAD_CAST "conflict"); 695 696 xmlTextWriterWriteElement( 697 writer, 698 BAD_CAST "path", 699 BAD_CAST res->path); 700 701 if(res->conflict_source) { 702 xmlTextWriterWriteElement( 703 writer, 704 BAD_CAST "source", 705 BAD_CAST res->conflict_source); 706 } 707 708 // </conflict> 709 xmlTextWriterEndElement(writer); 710 } 711 712 // end 713 xmlTextWriterEndElement(writer); 714 r = xmlTextWriterEndDocument(writer); 715 if(r < 0) { 716 xmlFreeTextWriter(writer); 717 return -1; 718 } 719 xmlFreeTextWriter(writer); 720 return 0; 721 } 722 723 void destroy_db(SyncDatabase *db) { 724 cxMapDestroy(db->resources); 725 cxMapDestroy(db->conflict); 726 free(db); 727 } 728 729 void local_resource_free(LocalResource *res) { 730 if(!res) { 731 return; 732 } 733 if(res->name) { 734 free(res->name); 735 } 736 if(res->path) { 737 free(res->path); 738 } 739 if(res->etag) { 740 free(res->etag); 741 } 742 if(res->cached_tags) { 743 cxBufferFree(res->cached_tags); 744 } 745 if(res->tags_hash) { 746 free(res->tags_hash); 747 } 748 if(res->prev_hash) { 749 free(res->prev_hash); 750 } 751 free(res); 752 } 753 754 static char* nullstrdup(const char *s) { 755 return s ? strdup(s) : NULL; 756 } 757 758 void local_resource_copy_parts(LocalResource *from, LocalResource *to) { 759 if(from->parts) { 760 to->numparts = from->numparts; 761 to->parts = calloc(from->numparts, sizeof(FilePart)); 762 for(int i=0;i<to->numparts;i++) { 763 FilePart s = from->parts[i]; 764 FilePart p; 765 p.block = s.block; 766 p.hash = nullstrdup(s.hash); 767 p.etag = nullstrdup(s.etag); 768 to->parts[i] = p; 769 } 770 } 771 } 772 773 LocalResource* local_resource_copy(LocalResource *src, const char *new_path) { 774 LocalResource *newres = calloc(1, sizeof(LocalResource)); 775 newres->path = strdup(new_path); 776 newres->etag = nullstrdup(src->etag); 777 newres->hash = nullstrdup(src->hash); 778 newres->last_modified = src->last_modified; 779 newres->mode = src->mode; 780 newres->uid = src->uid; 781 newres->gid = src->gid; 782 newres->size = src->size; 783 newres->isdirectory = src->isdirectory; 784 newres->skipped = src->skipped; 785 newres->versioncontrol = src->versioncontrol; 786 787 if(src->xattr) { 788 XAttributes *xattr = calloc(1, sizeof(XAttributes)); 789 xattr->hash = nullstrdup(src->xattr->hash); 790 xattr->nattr = src->xattr->nattr; 791 xattr->names = calloc(xattr->nattr, sizeof(char*)); 792 xattr->values = calloc(xattr->nattr, sizeof(cxmutstr)); 793 for(int i=0;i<xattr->nattr;i++) { 794 xattr->names[i] = strdup(src->xattr->names[i]); 795 xattr->values[i] = cx_strdup(cx_strcast(src->xattr->values[i])); 796 } 797 newres->xattr = xattr; 798 } 799 800 newres->tags_hash = nullstrdup(src->tags_hash); 801 newres->xattr_hash = nullstrdup(src->xattr_hash); 802 newres->remote_tags_hash = nullstrdup(src->remote_tags_hash); 803 804 local_resource_copy_parts(src, newres); 805 806 newres->blocksize = src->blocksize; 807 808 newres->tags_updated = src->tags_updated; 809 newres->finfo_updated = src->finfo_updated; 810 newres->xattr_updated = src->xattr_updated; 811 newres->metadata_updated = src->metadata_updated; 812 813 return newres; 814 815 } 816 817 void filepart_free(FilePart *part) { 818 if(part->etag) { 819 free(part->etag); 820 } 821 if(part->hash) { 822 free(part->hash); 823 } 824 free(part); 825 } 826 827 CxMap* create_hash_index(SyncDatabase *db) { 828 CxMap *hmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, db->resources->size + 64); 829 830 CxIterator i = cxMapIteratorValues(db->resources); 831 LocalResource *res; 832 cx_foreach(LocalResource*, res, i) { 833 if(res->hash) { 834 cxMapPut(hmap, cx_hash_key_str(res->hash), res); 835 } 836 } 837 838 return hmap; 839 } 840