1/* 2 * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6#include "compat.h" 7 8#include <fcntl.h> 9#include <stdio.h> 10#include <unistd.h> 11 12#include <AutoDeleter.h> 13#include <fs_attr.h> 14#include <StorageDefs.h> 15 16#include "kprotos.h" 17#include "path_util.h" 18#include "stat_util.h" 19#include "xcp.h" 20 21static void *sCopyBuffer = NULL; 22static const int sCopyBufferSize = 64 * 1024; // 64 KB 23 24struct Options { 25 Options() 26 : dereference(true), 27 force(false), 28 recursive(false) 29 { 30 } 31 32 bool dereference; 33 bool force; 34 bool recursive; 35}; 36 37class Directory; 38class File; 39class SymLink; 40 41// Node 42class Node { 43public: 44 Node() {} 45 virtual ~Node() {} 46 47 const my_stat &Stat() const { return fStat; } 48 bool IsFile() const { return MY_S_ISREG(fStat.mode); } 49 bool IsDirectory() const { return MY_S_ISDIR(fStat.mode); } 50 bool IsSymLink() const { return MY_S_ISLNK(fStat.mode); } 51 52 virtual File *ToFile() { return NULL; } 53 virtual Directory *ToDirectory() { return NULL; } 54 virtual SymLink *ToSymLink() { return NULL; } 55 56 virtual ssize_t GetNextAttr(char *name, int size) = 0; 57 virtual status_t GetAttrInfo(const char *name, my_attr_info &info) = 0; 58 virtual ssize_t ReadAttr(const char *name, uint32 type, fs_off_t pos, 59 void *buffer, int size) = 0; 60 virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos, 61 const void *buffer, int size) = 0; 62 virtual status_t RemoveAttr(const char *name) = 0; 63 64protected: 65 struct my_stat fStat; // To be initialized by implementing classes. 66}; 67 68// Directory 69class Directory : public virtual Node { 70public: 71 virtual Directory *ToDirectory() { return this; } 72 73 virtual ssize_t GetNextEntry(struct my_dirent *entry, int size) = 0; 74}; 75 76// File 77class File : public virtual Node { 78public: 79 virtual File *ToFile() { return this; } 80 81 virtual ssize_t Read(void *buffer, int size) = 0; 82 virtual ssize_t Write(const void *buffer, int size) = 0; 83}; 84 85// SymLink 86class SymLink : public virtual Node { 87public: 88 virtual SymLink *ToSymLink() { return this; } 89 90 virtual ssize_t ReadLink(char *buffer, int bufferSize) = 0; 91}; 92 93// FSDomain 94class FSDomain { 95public: 96 virtual status_t Open(const char *path, int openMode, Node *&node) = 0; 97 98 virtual status_t CreateFile(const char *path, const struct my_stat &st, 99 File *&file) = 0; 100 virtual status_t CreateDirectory(const char *path, const struct my_stat &st, 101 Directory *&dir) = 0; 102 virtual status_t CreateSymLink(const char *path, const char *linkTo, 103 const struct my_stat &st, SymLink *&link) = 0; 104 105 virtual status_t Unlink(const char *path) = 0; 106}; 107 108 109// #pragma mark - 110 111// HostNode 112class HostNode : public virtual Node { 113public: 114 HostNode() 115 : Node(), 116 fFD(-1), 117 fAttrDir(NULL) 118 { 119 } 120 121 virtual ~HostNode() 122 { 123 if (fFD >= 0) 124 close(fFD); 125 if (fAttrDir) 126 fs_close_attr_dir(fAttrDir); 127 } 128 129 virtual status_t Init(const char *path, int fd, const my_stat &st) 130 { 131 fFD = fd; 132 fStat = st; 133 134 // open the attribute directory 135 fAttrDir = fs_fopen_attr_dir(fd); 136 if (!fAttrDir) 137 return from_platform_error(errno); 138 139 return FS_OK; 140 } 141 142 virtual ssize_t GetNextAttr(char *name, int size) 143 { 144 if (!fAttrDir) 145 return 0; 146 147 errno = 0; 148 struct dirent *entry = fs_read_attr_dir(fAttrDir); 149 if (!entry) 150 return from_platform_error(errno); 151 152 int len = strlen(entry->d_name); 153 if (len >= size) 154 return FS_NAME_TOO_LONG; 155 156 strcpy(name, entry->d_name); 157 return 1; 158 } 159 160 virtual status_t GetAttrInfo(const char *name, my_attr_info &info) 161 { 162 attr_info hostInfo; 163 if (fs_stat_attr(fFD, name, &hostInfo) < 0) 164 return from_platform_error(errno); 165 166 info.type = hostInfo.type; 167 info.size = hostInfo.size; 168 return FS_OK; 169 } 170 171 virtual ssize_t ReadAttr(const char *name, uint32 type, fs_off_t pos, 172 void *buffer, int size) 173 { 174 ssize_t bytesRead = fs_read_attr(fFD, name, type, pos, buffer, size); 175 return (bytesRead >= 0 ? bytesRead : from_platform_error(errno)); 176 } 177 178 virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos, 179 const void *buffer, int size) 180 { 181 ssize_t bytesWritten = fs_write_attr(fFD, name, type, pos, buffer, 182 size); 183 return (bytesWritten >= 0 ? bytesWritten : from_platform_error(errno)); 184 } 185 186 virtual status_t RemoveAttr(const char *name) 187 { 188 return (fs_remove_attr(fFD, name) == 0 189 ? 0 : from_platform_error(errno)); 190 } 191 192protected: 193 int fFD; 194 DIR *fAttrDir; 195}; 196 197// HostDirectory 198class HostDirectory : public Directory, public HostNode { 199public: 200 HostDirectory() 201 : Directory(), 202 HostNode(), 203 fDir(NULL) 204 { 205 } 206 207 virtual ~HostDirectory() 208 { 209 if (fDir) 210 closedir(fDir); 211 } 212 213 virtual status_t Init(const char *path, int fd, const my_stat &st) 214 { 215 status_t error = HostNode::Init(path, fd, st); 216 if (error != FS_OK) 217 return error; 218 219 fDir = opendir(path); 220 if (!fDir) 221 return from_platform_error(errno); 222 223 return FS_OK; 224 } 225 226 virtual ssize_t GetNextEntry(struct my_dirent *entry, int size) 227 { 228 errno = 0; 229 struct dirent *hostEntry = readdir(fDir); 230 if (!hostEntry) 231 return from_platform_error(errno); 232 233 int nameLen = strlen(hostEntry->d_name); 234 int recLen = entry->d_name + nameLen + 1 - (char*)entry; 235 if (recLen > size) 236 return FS_NAME_TOO_LONG; 237 238 #if (defined(__BEOS__) || defined(__HAIKU__)) 239 entry->d_dev = hostEntry->d_dev; 240 #endif 241 entry->d_ino = hostEntry->d_ino; 242 strcpy(entry->d_name, hostEntry->d_name); 243 entry->d_reclen = recLen; 244 245 return 1; 246 } 247 248private: 249 DIR *fDir; 250}; 251 252// HostFile 253class HostFile : public File, public HostNode { 254public: 255 HostFile() 256 : File(), 257 HostNode() 258 { 259 } 260 261 virtual ~HostFile() 262 { 263 } 264 265 virtual ssize_t Read(void *buffer, int size) 266 { 267 ssize_t bytesRead = read(fFD, buffer, size); 268 return (bytesRead >= 0 ? bytesRead : from_platform_error(errno)); 269 } 270 271 virtual ssize_t Write(const void *buffer, int size) 272 { 273 ssize_t bytesWritten = write(fFD, buffer, size); 274 return (bytesWritten >= 0 ? bytesWritten : from_platform_error(errno)); 275 } 276}; 277 278// HostSymLink 279class HostSymLink : public SymLink, public HostNode { 280public: 281 HostSymLink() 282 : SymLink(), 283 HostNode() 284 { 285 } 286 287 virtual ~HostSymLink() 288 { 289 if (fPath) 290 free(fPath); 291 } 292 293 virtual status_t Init(const char *path, int fd, const my_stat &st) 294 { 295 status_t error = HostNode::Init(path, fd, st); 296 if (error != FS_OK) 297 return error; 298 299 fPath = strdup(path); 300 if (!fPath) 301 return FS_NO_MEMORY; 302 303 return FS_OK; 304 } 305 306 virtual ssize_t ReadLink(char *buffer, int bufferSize) 307 { 308 ssize_t bytesRead = readlink(fPath, buffer, bufferSize); 309 return (bytesRead >= 0 ? bytesRead : from_platform_error(errno)); 310 } 311 312private: 313 char *fPath; 314}; 315 316// HostFSDomain 317class HostFSDomain : public FSDomain { 318public: 319 HostFSDomain() {} 320 virtual ~HostFSDomain() {} 321 322 virtual status_t Open(const char *path, int openMode, Node *&_node) 323 { 324 // open the node 325 int fd = open(path, to_platform_open_mode(openMode)); 326 if (fd < 0) 327 return from_platform_error(errno); 328 329 // stat the node 330 struct stat st; 331 if (fstat(fd, &st) < 0) { 332 close(fd); 333 return from_platform_error(errno); 334 } 335 336 // check the node type and create the node 337 HostNode *node = NULL; 338 switch (st.st_mode & S_IFMT) { 339 case S_IFLNK: 340 node = new HostSymLink; 341 break; 342 case S_IFREG: 343 node = new HostFile; 344 break; 345 case S_IFDIR: 346 node = new HostDirectory; 347 break; 348 default: 349 close(fd); 350 return FS_EINVAL; 351 } 352 353 // convert the stat 354 struct my_stat myst; 355 from_platform_stat(&st, &myst); 356 357 // init the node 358 status_t error = node->Init(path, fd, myst); 359 // the node receives ownership of the FD 360 if (error != FS_OK) { 361 delete node; 362 return error; 363 } 364 365 _node = node; 366 return FS_OK; 367 } 368 369 virtual status_t CreateFile(const char *path, const struct my_stat &myst, 370 File *&_file) 371 { 372 struct stat st; 373 to_platform_stat(&myst, &st); 374 375 // create the file 376 int fd = creat(path, st.st_mode & S_IUMSK); 377 if (fd < 0) 378 return from_platform_error(errno); 379 380 // apply the other stat fields 381 status_t error = _ApplyStat(fd, st); 382 if (error != FS_OK) { 383 close(fd); 384 return error; 385 } 386 387 // create the object 388 HostFile *file = new HostFile; 389 error = file->Init(path, fd, myst); 390 if (error != FS_OK) { 391 delete file; 392 return error; 393 } 394 395 _file = file; 396 return FS_OK; 397 } 398 399 virtual status_t CreateDirectory(const char *path, 400 const struct my_stat &myst, Directory *&_dir) 401 { 402 struct stat st; 403 to_platform_stat(&myst, &st); 404 405 // create the dir 406 if (mkdir(path, st.st_mode & S_IUMSK) < 0) 407 return from_platform_error(errno); 408 409 // open the dir node 410 int fd = open(path, O_RDONLY | O_NOTRAVERSE); 411 if (fd < 0) 412 return from_platform_error(errno); 413 414 // apply the other stat fields 415 status_t error = _ApplyStat(fd, st); 416 if (error != FS_OK) { 417 close(fd); 418 return error; 419 } 420 421 // create the object 422 HostDirectory *dir = new HostDirectory; 423 error = dir->Init(path, fd, myst); 424 if (error != FS_OK) { 425 delete dir; 426 return error; 427 } 428 429 _dir = dir; 430 return FS_OK; 431 } 432 433 virtual status_t CreateSymLink(const char *path, const char *linkTo, 434 const struct my_stat &myst, SymLink *&_link) 435 { 436 struct stat st; 437 to_platform_stat(&myst, &st); 438 439 // create the link 440 if (symlink(linkTo, path) < 0) 441 return from_platform_error(errno); 442 443 // open the symlink node 444 int fd = open(path, O_RDONLY | O_NOTRAVERSE); 445 if (fd < 0) 446 return from_platform_error(errno); 447 448 // apply the other stat fields 449 status_t error = _ApplyStat(fd, st); 450 if (error != FS_OK) { 451 close(fd); 452 return error; 453 } 454 455 // create the object 456 HostSymLink *link = new HostSymLink; 457 error = link->Init(path, fd, myst); 458 if (error != FS_OK) { 459 delete link; 460 return error; 461 } 462 463 _link = link; 464 return FS_OK; 465 } 466 467 468 virtual status_t Unlink(const char *path) 469 { 470 if (unlink(path) < 0) 471 return from_platform_error(errno); 472 return FS_OK; 473 } 474 475private: 476 status_t _ApplyStat(int fd, const struct stat &st) 477 { 478 // TODO: Set times... 479 return FS_OK; 480 } 481}; 482 483 484// #pragma mark - 485 486// GuestNode 487class GuestNode : public virtual Node { 488public: 489 GuestNode() 490 : Node(), 491 fFD(-1), 492 fAttrDir(-1) 493 { 494 } 495 496 virtual ~GuestNode() 497 { 498 if (fFD >= 0) 499 sys_close(true, fFD); 500 if (fAttrDir) 501 sys_closedir(true, fAttrDir); 502 } 503 504 virtual status_t Init(const char *path, int fd, const my_stat &st) 505 { 506 fFD = fd; 507 fStat = st; 508 509 // open the attribute directory 510 fAttrDir = sys_open_attr_dir(true, fd, NULL); 511 if (fAttrDir < 0) 512 return fAttrDir; 513 514 return FS_OK; 515 } 516 517 virtual ssize_t GetNextAttr(char *name, int size) 518 { 519 if (fAttrDir < 0) 520 return 0; 521 522 char buffer[sizeof(my_dirent) + B_ATTR_NAME_LENGTH]; 523 struct my_dirent *entry = (my_dirent *)buffer; 524 int numRead = sys_readdir(true, fAttrDir, entry, sizeof(buffer), 1); 525 if (numRead < 0) 526 return numRead; 527 if (numRead == 0) 528 return 0; 529 530 int len = strlen(entry->d_name); 531 if (len >= size) 532 return FS_NAME_TOO_LONG; 533 534 strcpy(name, entry->d_name); 535 return 1; 536 } 537 538 virtual status_t GetAttrInfo(const char *name, my_attr_info &info) 539 { 540 return sys_stat_attr(true, fFD, NULL, name, &info); 541 } 542 543 virtual ssize_t ReadAttr(const char *name, uint32 type, fs_off_t pos, 544 void *buffer, int size) 545 { 546 return sys_read_attr(true, fFD, name, type, buffer, size, pos); 547 } 548 549 virtual ssize_t WriteAttr(const char *name, uint32 type, fs_off_t pos, 550 const void *buffer, int size) 551 { 552 return sys_write_attr(true, fFD, name, type, buffer, size, pos); 553 } 554 555 virtual status_t RemoveAttr(const char *name) 556 { 557 return sys_remove_attr(true, fFD, name); 558 } 559 560protected: 561 int fFD; 562 int fAttrDir; 563}; 564 565// GuestDirectory 566class GuestDirectory : public Directory, public GuestNode { 567public: 568 GuestDirectory() 569 : Directory(), 570 GuestNode(), 571 fDir(-1) 572 { 573 } 574 575 virtual ~GuestDirectory() 576 { 577 if (fDir) 578 sys_closedir(true, fDir); 579 } 580 581 virtual status_t Init(const char *path, int fd, const my_stat &st) 582 { 583 status_t error = GuestNode::Init(path, fd, st); 584 if (error != FS_OK) 585 return error; 586 587 fDir = sys_opendir(true, fd, NULL, true); 588 if (fDir < 0) 589 return fDir; 590 591 return FS_OK; 592 } 593 594 virtual ssize_t GetNextEntry(struct my_dirent *entry, int size) 595 { 596 return sys_readdir(true, fDir, entry, size, 1); 597 } 598 599private: 600 int fDir; 601}; 602 603// GuestFile 604class GuestFile : public File, public GuestNode { 605public: 606 GuestFile() 607 : File(), 608 GuestNode() 609 { 610 } 611 612 virtual ~GuestFile() 613 { 614 } 615 616 virtual ssize_t Read(void *buffer, int size) 617 { 618 return sys_read(true, fFD, buffer, size); 619 } 620 621 virtual ssize_t Write(const void *buffer, int size) 622 { 623 return sys_write(true, fFD, buffer, size); 624 } 625}; 626 627// GuestSymLink 628class GuestSymLink : public SymLink, public GuestNode { 629public: 630 GuestSymLink() 631 : SymLink(), 632 GuestNode() 633 { 634 } 635 636 virtual ~GuestSymLink() 637 { 638 } 639 640 virtual ssize_t ReadLink(char *buffer, int bufferSize) 641 { 642 return sys_readlink(true, fFD, NULL, buffer, bufferSize); 643 } 644}; 645 646// GuestFSDomain 647class GuestFSDomain : public FSDomain { 648public: 649 GuestFSDomain() {} 650 virtual ~GuestFSDomain() {} 651 652 virtual status_t Open(const char *path, int openMode, Node *&_node) 653 { 654 // open the node 655 int fd = sys_open(true, -1, path, openMode, 0, true); 656 if (fd < 0) 657 return fd; 658 659 // stat the node 660 struct my_stat st; 661 status_t error = sys_rstat(true, fd, NULL, &st, false); 662 if (error < 0) { 663 sys_close(true, fd); 664 return error; 665 } 666 667 // check the node type and create the node 668 GuestNode *node = NULL; 669 switch (st.mode & MY_S_IFMT) { 670 case MY_S_IFLNK: 671 node = new GuestSymLink; 672 break; 673 case MY_S_IFREG: 674 node = new GuestFile; 675 break; 676 case MY_S_IFDIR: 677 node = new GuestDirectory; 678 break; 679 default: 680 sys_close(true, fd); 681 return FS_EINVAL; 682 } 683 684 // init the node 685 error = node->Init(path, fd, st); 686 // the node receives ownership of the FD 687 if (error != FS_OK) { 688 delete node; 689 return error; 690 } 691 692 _node = node; 693 return FS_OK; 694 } 695 696 virtual status_t CreateFile(const char *path, const struct my_stat &st, 697 File *&_file) 698 { 699 // create the file 700 int fd = sys_open(true, -1, path, MY_O_RDWR | MY_O_EXCL | MY_O_CREAT, 701 st.mode & MY_S_IUMSK, true); 702 if (fd < 0) 703 return fd; 704 705 // apply the other stat fields 706 status_t error = _ApplyStat(fd, st); 707 if (error != FS_OK) { 708 sys_close(true, fd); 709 return error; 710 } 711 712 // create the object 713 GuestFile *file = new GuestFile; 714 error = file->Init(path, fd, st); 715 if (error != FS_OK) { 716 delete file; 717 return error; 718 } 719 720 _file = file; 721 return FS_OK; 722 } 723 724 virtual status_t CreateDirectory(const char *path, const struct my_stat &st, 725 Directory *&_dir) 726 { 727 // create the dir 728 status_t error = sys_mkdir(true, -1, path, st.mode & MY_S_IUMSK); 729 if (error < 0) 730 return error; 731 732 // open the dir node 733 int fd = sys_open(true, -1, path, MY_O_RDONLY | MY_O_NOTRAVERSE, 734 0, true); 735 if (fd < 0) 736 return fd; 737 738 // apply the other stat fields 739 error = _ApplyStat(fd, st); 740 if (error != FS_OK) { 741 sys_close(true, fd); 742 return error; 743 } 744 745 // create the object 746 GuestDirectory *dir = new GuestDirectory; 747 error = dir->Init(path, fd, st); 748 if (error != FS_OK) { 749 delete dir; 750 return error; 751 } 752 753 _dir = dir; 754 return FS_OK; 755 } 756 757 virtual status_t CreateSymLink(const char *path, const char *linkTo, 758 const struct my_stat &st, SymLink *&_link) 759 { 760 // create the link 761 status_t error = sys_symlink(true, linkTo, -1, path); 762 if (error < 0) 763 return error; 764 765 // open the symlink node 766 int fd = sys_open(true, -1, path, MY_O_RDONLY | MY_O_NOTRAVERSE, 767 0, true); 768 if (fd < 0) 769 return fd; 770 771 // apply the other stat fields 772 error = _ApplyStat(fd, st); 773 if (error != FS_OK) { 774 sys_close(true, fd); 775 return error; 776 } 777 778 // create the object 779 GuestSymLink *link = new GuestSymLink; 780 error = link->Init(path, fd, st); 781 if (error != FS_OK) { 782 delete link; 783 return error; 784 } 785 786 _link = link; 787 return FS_OK; 788 } 789 790 virtual status_t Unlink(const char *path) 791 { 792 return sys_unlink(true, -1, path); 793 } 794 795private: 796 status_t _ApplyStat(int fd, const struct my_stat &st) 797 { 798 // TODO: Set times... 799 return FS_OK; 800 } 801}; 802 803 804// #pragma mark - 805 806static status_t copy_entry(FSDomain *sourceDomain, const char *source, 807 FSDomain *targetDomain, const char *target, const Options &options); 808 809static FSDomain * 810get_file_domain(const char *target, const char *&fsTarget) 811{ 812 if (target[0] == ':') { 813 fsTarget = target + 1; 814 return new HostFSDomain; 815 } else { 816 fsTarget = target; 817 return new GuestFSDomain; 818 } 819} 820 821typedef ObjectDeleter<Node> NodeDeleter; 822typedef ObjectDeleter<FSDomain> DomainDeleter; 823typedef MemoryDeleter PathDeleter; 824 825 826static status_t 827copy_file_contents(const char *source, File *sourceFile, const char *target, 828 File *targetFile) 829{ 830 ssize_t bytesRead; 831 while ((bytesRead = sourceFile->Read(sCopyBuffer, sCopyBufferSize)) > 0) { 832 ssize_t bytesWritten = targetFile->Write(sCopyBuffer, bytesRead); 833 if (bytesWritten < 0) { 834 fprintf(stderr, "Error while writing to file `%s': %s\n", 835 target, fs_strerror(bytesWritten)); 836 return bytesWritten; 837 } 838 } 839 840 if (bytesRead < 0) { 841 fprintf(stderr, "Error while reading from file `%s': %s\n", 842 source, fs_strerror(bytesRead)); 843 return bytesRead; 844 } 845 846 return FS_OK; 847} 848 849 850static status_t 851copy_dir_contents(FSDomain *sourceDomain, const char *source, 852 Directory *sourceDir, FSDomain *targetDomain, const char *target, 853 const Options &options) 854{ 855 char buffer[sizeof(my_dirent) + B_FILE_NAME_LENGTH]; 856 struct my_dirent *entry = (struct my_dirent *)buffer; 857 ssize_t numRead; 858 while ((numRead = sourceDir->GetNextEntry(entry, sizeof(buffer))) > 0) { 859 // skip "." and ".." 860 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) 861 continue; 862 863 // compose a new source path name 864 char *sourceEntry = make_path(source, entry->d_name); 865 if (!sourceEntry) { 866 fprintf(stderr, "Failed to allocate source path!\n"); 867 return FS_ENOMEM; 868 } 869 PathDeleter sourceDeleter(sourceEntry); 870 871 // compose a new target path name 872 char *targetEntry = make_path(target, entry->d_name); 873 if (!targetEntry) { 874 fprintf(stderr, "Failed to allocate target path!\n"); 875 return FS_ENOMEM; 876 } 877 PathDeleter targetDeleter(targetEntry); 878 879 status_t error = copy_entry(sourceDomain, sourceEntry, targetDomain, 880 targetEntry, options); 881 if (error != FS_OK) 882 return error; 883 } 884 885 if (numRead < 0) { 886 fprintf(stderr, "Error reading directory `%s': %s\n", source, 887 fs_strerror(numRead)); 888 return numRead; 889 } 890 891 return FS_OK; 892} 893 894 895static status_t 896copy_attribute(const char *source, Node *sourceNode, const char *target, 897 Node *targetNode, const char *name, const my_attr_info &info) 898{ 899 // remove the attribute first 900 targetNode->RemoveAttr(name); 901 902 // special case: empty attribute 903 if (info.size <= 0) { 904 ssize_t bytesWritten = targetNode->WriteAttr(name, info.type, 0, 905 sCopyBuffer, 0); 906 if (bytesWritten) { 907 fprintf(stderr, "Error while writing to attribute `%s' of file " 908 "`%s': %s\n", name, target, fs_strerror(bytesWritten)); 909 return bytesWritten; 910 } 911 912 return FS_OK; 913 } 914 915 // non-empty attribute 916 fs_off_t pos = 0; 917 int toCopy = info.size; 918 while (toCopy > 0) { 919 // read data from source 920 int toRead = (toCopy < sCopyBufferSize ? toCopy : sCopyBufferSize); 921 ssize_t bytesRead = sourceNode->ReadAttr(name, info.type, pos, 922 sCopyBuffer, toRead); 923 if (bytesRead < 0) { 924 fprintf(stderr, "Error while reading from attribute `%s' of file " 925 "`%s': %s\n", name, source, fs_strerror(bytesRead)); 926 return bytesRead; 927 } 928 929 // write data to target 930 ssize_t bytesWritten = targetNode->WriteAttr(name, info.type, pos, 931 sCopyBuffer, bytesRead); 932 if (bytesWritten < 0) { 933 fprintf(stderr, "Error while writing to attribute `%s' of file " 934 "`%s': %s\n", name, target, fs_strerror(bytesWritten)); 935 return bytesWritten; 936 } 937 938 pos += bytesRead; 939 toCopy -= bytesRead; 940 } 941 942 return FS_OK; 943} 944 945 946static status_t 947copy_attributes(const char *source, Node *sourceNode, const char *target, 948 Node *targetNode) 949{ 950 char name[B_ATTR_NAME_LENGTH]; 951 ssize_t numRead; 952 while ((numRead = sourceNode->GetNextAttr(name, sizeof(name))) > 0) { 953 my_attr_info info; 954 // get attribute info 955 status_t error = sourceNode->GetAttrInfo(name, info); 956 if (error != FS_OK) { 957 fprintf(stderr, "Error getting info for attribute `%s' of file " 958 "`%s': %s\n", name, source, fs_strerror(error)); 959 return error; 960 } 961 962 // copy the attribute 963 error = copy_attribute(source, sourceNode, target, targetNode, name, 964 info); 965 if (error != FS_OK) 966 return error; 967 } 968 969 if (numRead < 0) { 970 fprintf(stderr, "Error reading attribute directory of `%s': %s\n", 971 source, fs_strerror(numRead)); 972 return numRead; 973 } 974 975 return FS_OK; 976} 977 978 979static status_t 980copy_entry(FSDomain *sourceDomain, const char *source, 981 FSDomain *targetDomain, const char *target, const Options &options) 982{ 983 // open the source node 984 Node *sourceNode; 985 status_t error = sourceDomain->Open(source, 986 MY_O_RDONLY | (options.dereference ? 0 : MY_O_NOTRAVERSE), 987 sourceNode); 988 if (error != FS_OK) { 989 fprintf(stderr, "Failed to open source path `%s': %s\n", source, 990 fs_strerror(error)); 991 return error; 992 } 993 NodeDeleter sourceDeleter(sourceNode); 994 995 // check, if target exists 996 Node *targetNode = NULL; 997 // try opening with resolving symlinks first 998 error = targetDomain->Open(target, MY_O_RDONLY | MY_O_NOTRAVERSE, 999 targetNode); 1000 NodeDeleter targetDeleter; 1001 if (error == FS_OK) { 1002 // 1. target exists: 1003 // check, if it is a dir and, if so, whether source is a dir too 1004 targetDeleter.SetTo(targetNode); 1005 1006 // if the target is a symlink, try resolving it 1007 if (targetNode->IsSymLink()) { 1008 Node *resolvedTargetNode; 1009 error = targetDomain->Open(target, MY_O_RDONLY, resolvedTargetNode); 1010 if (error == FS_OK) { 1011 targetNode = resolvedTargetNode; 1012 targetDeleter.SetTo(targetNode); 1013 } 1014 } 1015 1016 if (sourceNode->IsDirectory() && targetNode->IsDirectory()) { 1017 // 1.1. target and source are dirs: 1018 // -> just copy their contents 1019 // ... 1020 } else { 1021 // 1.2. source and/or target are no dirs 1022 1023 if (options.force) { 1024 // 1.2.1. /force/ 1025 // -> remove the target and continue with 2. 1026 targetDeleter.Delete(); 1027 targetNode = NULL; 1028 error = targetDomain->Unlink(target); 1029 if (error != FS_OK) { 1030 fprintf(stderr, "Failed to remove `%s'\n", target); 1031 return error; 1032 } 1033 } else if (sourceNode->IsFile() && targetNode->IsFile()) { 1034 // 1.2.1.1. !/force/, but both source and target are files 1035 // -> truncate the target file and continue 1036 targetDeleter.Delete(); 1037 targetNode = NULL; 1038 error = targetDomain->Open(target, MY_O_RDWR | MY_O_TRUNC, 1039 targetNode); 1040 if (error != FS_OK) { 1041 fprintf(stderr, "Failed to open `%s' for writing\n", 1042 target); 1043 return error; 1044 } 1045 } else { 1046 // 1.2.1.2. !/force/, source or target isn't a file 1047 // -> fail 1048 fprintf(stderr, "File `%s' does exist.\n", target); 1049 return FS_FILE_EXISTS; 1050 } 1051 } 1052 } // else: 2. target doesn't exist: -> just create it 1053 1054 // create the target node 1055 error = FS_OK; 1056 if (sourceNode->IsFile()) { 1057 if (!targetNode) { 1058 File *file = NULL; 1059 error = targetDomain->CreateFile(target, sourceNode->Stat(), file); 1060 if (error == 0) 1061 targetNode = file; 1062 } 1063 } else if (sourceNode->IsDirectory()) { 1064 // check /recursive/ 1065 if (!options.recursive) { 1066 fprintf(stderr, "Entry `%s' is a directory.\n", source); 1067 return FS_EISDIR; 1068 } 1069 1070 // create the target only, if it doesn't already exist 1071 if (!targetNode) { 1072 Directory *dir = NULL; 1073 error = targetDomain->CreateDirectory(target, sourceNode->Stat(), 1074 dir); 1075 if (error == 0) 1076 targetNode = dir; 1077 } 1078 } else if (sourceNode->IsSymLink()) { 1079 // read the source link 1080 SymLink *sourceLink = sourceNode->ToSymLink(); 1081 char linkTo[B_PATH_NAME_LENGTH]; 1082 ssize_t bytesRead = sourceLink->ReadLink(linkTo, sizeof(linkTo) - 1); 1083 if (bytesRead < 0) { 1084 fprintf(stderr, "Failed to read symlink `%s': %s\n", source, 1085 fs_strerror(bytesRead)); 1086 } 1087 linkTo[bytesRead] = '\0'; // always NULL-terminate 1088 1089 // create the target link 1090 SymLink *link; 1091 error = targetDomain->CreateSymLink(target, linkTo, 1092 sourceNode->Stat(), link); 1093 if (error == 0) 1094 targetNode = link; 1095 } else { 1096 fprintf(stderr, "Unknown node type. We shouldn't be here!\n"); 1097 return FS_EINVAL; 1098 } 1099 1100 if (error != FS_OK) { 1101 fprintf(stderr, "Failed to create `%s': %s\n", target, 1102 fs_strerror(error)); 1103 return error; 1104 } 1105 targetDeleter.SetTo(targetNode); 1106 1107 // copy attributes 1108 error = copy_attributes(source, sourceNode, target, targetNode); 1109 if (error != FS_OK) 1110 return error; 1111 1112 // copy contents 1113 if (sourceNode->IsFile()) { 1114 error = copy_file_contents(source, sourceNode->ToFile(), target, 1115 targetNode->ToFile()); 1116 } else if (sourceNode->IsDirectory()) { 1117 error = copy_dir_contents(sourceDomain, source, 1118 sourceNode->ToDirectory(), targetDomain, target, options); 1119 } 1120 1121 return error; 1122} 1123 1124 1125int 1126do_xcp(int argc, char **argv) 1127{ 1128 int sourceCount = 0; 1129 Options options; 1130 1131 const char **sources = new const char*[argc]; 1132 if (!sources) { 1133 fprintf(stderr, "No memory!\n"); 1134 return FS_EINVAL; 1135 } 1136 ArrayDeleter<const char*> _(sources); 1137 1138 // parse parameters 1139 for (int argi = 1; argi < argc; argi++) { 1140 const char *arg = argv[argi]; 1141 if (arg[0] == '-') { 1142 if (arg[1] == '\0') { 1143 fprintf(stderr, "Invalid option `-'\n"); 1144 return FS_EINVAL; 1145 } 1146 1147 for (int i = 1; arg[i]; i++) { 1148 switch (arg[i]) { 1149 case 'd': 1150 options.dereference = false; 1151 break; 1152 case 'f': 1153 options.force = true; 1154 break; 1155 case 'r': 1156 options.recursive = true; 1157 break; 1158 default: 1159 fprintf(stderr, "Unknown option `-%c'\n", arg[i]); 1160 return FS_EINVAL; 1161 } 1162 } 1163 } else { 1164 sources[sourceCount++] = arg; 1165 } 1166 } 1167 1168 // check params 1169 if (sourceCount < 2) { 1170 fprintf(stderr, "Must specify at least 2 files!\n"); 1171 return FS_EINVAL; 1172 } 1173 1174 // check the target 1175 const char *target = sources[--sourceCount]; 1176 bool targetIsDir = false; 1177 bool targetExists = false; 1178 FSDomain *targetDomain = get_file_domain(target, target); 1179 DomainDeleter targetDomainDeleter(targetDomain); 1180 1181 Node *targetNode; 1182 status_t error = targetDomain->Open(target, MY_O_RDONLY, targetNode); 1183 if (error == 0) { 1184 NodeDeleter targetDeleter(targetNode); 1185 targetExists = true; 1186 1187 if (targetNode->IsDirectory()) { 1188 targetIsDir = true; 1189 } else { 1190 if (sourceCount > 1) { 1191 fprintf(stderr, "Destination `%s' is not a directory!", 1192 target); 1193 return FS_NOT_A_DIRECTORY; 1194 } 1195 } 1196 } else { 1197 if (sourceCount > 1) { 1198 fprintf(stderr, "Failed to open destination directory `%s': `%s'\n", 1199 target, fs_strerror(error)); 1200 return error; 1201 } 1202 } 1203 1204 // allocate a copy buffer 1205 sCopyBuffer = malloc(sCopyBufferSize); 1206 if (!sCopyBuffer) { 1207 fprintf(stderr, "Failed to allocate copy buffer.\n"); 1208 return FS_ENOMEM; 1209 } 1210 MemoryDeleter copyBufferDeleter(sCopyBuffer); 1211 1212 // the copy loop 1213 for (int i = 0; i < sourceCount; i++) { 1214 const char *source = sources[i]; 1215 FSDomain *sourceDomain = get_file_domain(source, source); 1216 DomainDeleter sourceDomainDeleter(sourceDomain); 1217 if (targetExists && targetIsDir) { 1218 // 1. target exists: 1219 // 1.1. target is a dir: 1220 // get the source leaf name 1221 char leafName[B_FILE_NAME_LENGTH]; 1222 error = get_last_path_component(source, leafName, sizeof(leafName)); 1223 if (error != FS_OK) { 1224 fprintf(stderr, "Failed to get last path component of `%s': " 1225 "%s\n", source, fs_strerror(error)); 1226 return error; 1227 } 1228 1229 if (strcmp(leafName, ".") == 0 || strcmp(leafName, "..") == 0) { 1230 // 1.1.1. source name is `.' or `..' 1231 // -> copy the contents only 1232 // (copy_dir_contents()) 1233 // open the source dir 1234 Node *sourceNode; 1235 error = sourceDomain->Open(source, 1236 MY_O_RDONLY | (options.dereference ? 0 : MY_O_NOTRAVERSE), 1237 sourceNode); 1238 if (error != FS_OK) { 1239 fprintf(stderr, "Failed to open `%s': %s.\n", source, 1240 fs_strerror(error)); 1241 return error; 1242 } 1243 NodeDeleter sourceDeleter(sourceNode); 1244 1245 // check, if it is a dir 1246 Directory *sourceDir = sourceNode->ToDirectory(); 1247 if (!sourceDir) { 1248 fprintf(stderr, "Source `%s' is not a directory although" 1249 "it's last path component is `%s'\n", source, leafName); 1250 return FS_EINVAL; 1251 } 1252 1253 error = copy_dir_contents(sourceDomain, source, sourceDir, 1254 targetDomain, target, options); 1255 } else { 1256 // 1.1.2. source has normal name 1257 // -> we copy into the dir 1258 // (copy_entry(<source>, <target>/<source leaf>)) 1259 // compose a new target path name 1260 char *targetEntry = make_path(target, leafName); 1261 if (!targetEntry) { 1262 fprintf(stderr, "Failed to allocate target path!\n"); 1263 return FS_ENOMEM; 1264 } 1265 PathDeleter targetDeleter(targetEntry); 1266 1267 error = copy_entry(sourceDomain, source, targetDomain, 1268 targetEntry, options); 1269 } 1270 } else { 1271 // 1.2. target is no dir: 1272 // -> if /force/ is given, we replace the target, otherwise 1273 // we fail 1274 // (copy_entry(<source>, <target>)) 1275 // or 1276 // 2. target doesn't exist: 1277 // -> we create the target as a clone of the source 1278 // (copy_entry(<source>, <target>)) 1279 error = copy_entry(sourceDomain, source, targetDomain, target, 1280 options); 1281 } 1282 1283 if (error != 0) 1284 return error; 1285 } 1286 1287 return 0; 1288} 1289 1290