1 /* 2 * Copyright 2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 */ 8 9 10 #include <CopyEngine.h> 11 12 #include <errno.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <unistd.h> 17 18 #include <Directory.h> 19 #include <Entry.h> 20 #include <File.h> 21 #include <fs_attr.h> 22 #include <Path.h> 23 #include <SymLink.h> 24 #include <TypeConstants.h> 25 26 27 namespace BPrivate { 28 29 30 static const size_t kDefaultBufferSize = 1024 * 1024; 31 static const size_t kSmallBufferSize = 64 * 1024; 32 33 34 // #pragma mark - BCopyEngine 35 36 37 BCopyEngine::BCopyEngine(uint32 flags) 38 : 39 fController(NULL), 40 fFlags(flags), 41 fBuffer(NULL), 42 fBufferSize(0) 43 { 44 } 45 46 47 BCopyEngine::~BCopyEngine() 48 { 49 delete[] fBuffer; 50 } 51 52 53 BCopyEngine::BController* 54 BCopyEngine::Controller() const 55 { 56 return fController; 57 } 58 59 60 void 61 BCopyEngine::SetController(BController* controller) 62 { 63 fController = controller; 64 } 65 66 67 uint32 68 BCopyEngine::Flags() const 69 { 70 return fFlags; 71 } 72 73 74 BCopyEngine& 75 BCopyEngine::SetFlags(uint32 flags) 76 { 77 fFlags = flags; 78 return *this; 79 } 80 81 82 BCopyEngine& 83 BCopyEngine::AddFlags(uint32 flags) 84 { 85 fFlags |= flags; 86 return *this; 87 } 88 89 90 BCopyEngine& 91 BCopyEngine::RemoveFlags(uint32 flags) 92 { 93 fFlags &= ~flags; 94 return *this; 95 } 96 97 98 status_t 99 BCopyEngine::CopyEntry(const Entry& sourceEntry, const Entry& destEntry) 100 { 101 if (fBuffer == NULL) { 102 fBuffer = new(std::nothrow) char[kDefaultBufferSize]; 103 if (fBuffer == NULL) { 104 fBuffer = new(std::nothrow) char[kSmallBufferSize]; 105 if (fBuffer == NULL) { 106 _NotifyError(B_NO_MEMORY, "Failed to allocate buffer"); 107 return B_NO_MEMORY; 108 } 109 fBufferSize = kSmallBufferSize; 110 } else 111 fBufferSize = kDefaultBufferSize; 112 } 113 114 BPath sourcePathBuffer; 115 const char* sourcePath; 116 status_t error = sourceEntry.GetPath(sourcePathBuffer, sourcePath); 117 if (error != B_OK) 118 return error; 119 120 BPath destPathBuffer; 121 const char* destPath; 122 error = destEntry.GetPath(destPathBuffer, destPath); 123 if (error != B_OK) 124 return error; 125 126 return _CopyEntry(sourcePath, destPath); 127 } 128 129 130 status_t 131 BCopyEngine::_CopyEntry(const char* sourcePath, const char* destPath) 132 { 133 // apply entry filter 134 if (fController != NULL && !fController->EntryStarted(sourcePath)) 135 return B_OK; 136 137 // stat source 138 struct stat sourceStat; 139 if (lstat(sourcePath, &sourceStat) < 0) { 140 return _HandleEntryError(sourcePath, errno, 141 "Couldn't access \"%s\": %s\n", sourcePath, strerror(errno)); 142 } 143 144 // stat destination 145 struct stat destStat; 146 bool destExists = lstat(destPath, &destStat) == 0; 147 148 // check whether to delete/create the destination 149 bool unlinkDest = destExists; 150 bool createDest = true; 151 if (destExists) { 152 if (S_ISDIR(destStat.st_mode)) { 153 if (!S_ISDIR(sourceStat.st_mode) 154 || (fFlags & MERGE_EXISTING_DIRECTORIES) == 0) { 155 return _HandleEntryError(sourcePath, B_FILE_EXISTS, 156 "Can't copy \"%s\", since directory \"%s\" is in the " 157 "way.\n", sourcePath, destPath); 158 } 159 160 if (S_ISDIR(sourceStat.st_mode)) { 161 // both are dirs; nothing to do 162 unlinkDest = false; 163 destExists = false; 164 } 165 } else if ((fFlags & UNLINK_DESTINATION) == 0) { 166 return _HandleEntryError(sourcePath, B_FILE_EXISTS, 167 "Can't copy \"%s\", since entry \"%s\" is in the way.\n", 168 sourcePath, destPath); 169 } 170 } 171 172 // unlink the destination 173 if (unlinkDest) { 174 if (unlink(destPath) < 0) { 175 return _HandleEntryError(sourcePath, errno, 176 "Failed to unlink \"%s\": %s\n", destPath, strerror(errno)); 177 } 178 } 179 180 // open source node 181 BNode _sourceNode; 182 BFile sourceFile; 183 BDirectory sourceDir; 184 BNode* sourceNode = NULL; 185 status_t error; 186 187 if (S_ISDIR(sourceStat.st_mode)) { 188 error = sourceDir.SetTo(sourcePath); 189 sourceNode = &sourceDir; 190 } else if (S_ISREG(sourceStat.st_mode)) { 191 error = sourceFile.SetTo(sourcePath, B_READ_ONLY); 192 sourceNode = &sourceFile; 193 } else { 194 error = _sourceNode.SetTo(sourcePath); 195 sourceNode = &_sourceNode; 196 } 197 198 if (error != B_OK) { 199 return _HandleEntryError(sourcePath, error, 200 "Failed to open \"%s\": %s\n", sourcePath, strerror(error)); 201 } 202 203 // create the destination 204 BNode _destNode; 205 BDirectory destDir; 206 BFile destFile; 207 BSymLink destSymLink; 208 BNode* destNode = NULL; 209 210 if (createDest) { 211 if (S_ISDIR(sourceStat.st_mode)) { 212 // create dir 213 error = BDirectory().CreateDirectory(destPath, &destDir); 214 if (error != B_OK) { 215 return _HandleEntryError(sourcePath, error, 216 "Failed to make directory \"%s\": %s\n", destPath, 217 strerror(error)); 218 } 219 220 destNode = &destDir; 221 } else if (S_ISREG(sourceStat.st_mode)) { 222 // create file 223 error = BDirectory().CreateFile(destPath, &destFile); 224 if (error != B_OK) { 225 return _HandleEntryError(sourcePath, error, 226 "Failed to create file \"%s\": %s\n", destPath, 227 strerror(error)); 228 } 229 230 destNode = &destFile; 231 232 // copy file contents 233 error = _CopyFileData(sourcePath, sourceFile, destPath, destFile); 234 if (error != B_OK) { 235 if (fController != NULL 236 && fController->EntryFinished(sourcePath, error)) { 237 return B_OK; 238 } 239 return error; 240 } 241 } else if (S_ISLNK(sourceStat.st_mode)) { 242 // read symlink 243 char* linkTo = fBuffer; 244 ssize_t bytesRead = readlink(sourcePath, linkTo, fBufferSize - 1); 245 if (bytesRead < 0) { 246 return _HandleEntryError(sourcePath, errno, 247 "Failed to read symlink \"%s\": %s\n", sourcePath, 248 strerror(errno)); 249 } 250 251 // null terminate the link contents 252 linkTo[bytesRead] = '\0'; 253 254 // create symlink 255 error = BDirectory().CreateSymLink(destPath, linkTo, &destSymLink); 256 if (error != B_OK) { 257 return _HandleEntryError(sourcePath, error, 258 "Failed to create symlink \"%s\": %s\n", destPath, 259 strerror(error)); 260 } 261 262 destNode = &destSymLink; 263 264 } else { 265 return _HandleEntryError(sourcePath, B_NOT_SUPPORTED, 266 "Source file \"%s\" has unsupported type.\n", sourcePath); 267 } 268 269 // copy attributes (before setting the permissions!) 270 error = _CopyAttributes(sourcePath, *sourceNode, destPath, *destNode); 271 if (error != B_OK) { 272 if (fController != NULL 273 && fController->EntryFinished(sourcePath, error)) { 274 return B_OK; 275 } 276 return error; 277 } 278 279 // set file owner, group, permissions, times 280 destNode->SetOwner(sourceStat.st_uid); 281 destNode->SetGroup(sourceStat.st_gid); 282 destNode->SetPermissions(sourceStat.st_mode); 283 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 284 destNode->SetCreationTime(sourceStat.st_crtime); 285 #endif 286 destNode->SetModificationTime(sourceStat.st_mtime); 287 } 288 289 // the destination node is no longer needed 290 destNode->Unset(); 291 292 // recurse 293 if ((fFlags & COPY_RECURSIVELY) != 0 && S_ISDIR(sourceStat.st_mode)) { 294 char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH]; 295 dirent *entry = (dirent*)buffer; 296 while (sourceDir.GetNextDirents(entry, sizeof(buffer), 1) == 1) { 297 if (strcmp(entry->d_name, ".") == 0 298 || strcmp(entry->d_name, "..") == 0) { 299 continue; 300 } 301 302 // construct new entry paths 303 BPath sourceEntryPath; 304 error = sourceEntryPath.SetTo(sourcePath, entry->d_name); 305 if (error != B_OK) { 306 return _HandleEntryError(sourcePath, error, 307 "Failed to construct entry path from dir \"%s\" and name " 308 "\"%s\": %s\n", sourcePath, entry->d_name, strerror(error)); 309 } 310 311 BPath destEntryPath; 312 error = destEntryPath.SetTo(destPath, entry->d_name); 313 if (error != B_OK) { 314 return _HandleEntryError(sourcePath, error, 315 "Failed to construct entry path from dir \"%s\" and name " 316 "\"%s\": %s\n", destPath, entry->d_name, strerror(error)); 317 } 318 319 // copy the entry 320 error = _CopyEntry(sourceEntryPath.Path(), destEntryPath.Path()); 321 if (error != B_OK) { 322 if (fController != NULL 323 && fController->EntryFinished(sourcePath, error)) { 324 return B_OK; 325 } 326 return error; 327 } 328 } 329 } 330 331 if (fController != NULL) 332 fController->EntryFinished(sourcePath, B_OK); 333 return B_OK; 334 } 335 336 337 status_t 338 BCopyEngine::_CopyFileData(const char* sourcePath, BFile& source, 339 const char* destPath, BFile& destination) 340 { 341 off_t offset = 0; 342 while (true) { 343 // read 344 ssize_t bytesRead = source.ReadAt(offset, fBuffer, fBufferSize); 345 if (bytesRead < 0) { 346 _NotifyError(bytesRead, "Failed to read from file \"%s\": %s\n", 347 sourcePath, strerror(bytesRead)); 348 return bytesRead; 349 } 350 351 if (bytesRead == 0) 352 return B_OK; 353 354 // write 355 ssize_t bytesWritten = destination.WriteAt(offset, fBuffer, bytesRead); 356 if (bytesWritten < 0) { 357 _NotifyError(bytesWritten, "Failed to write to file \"%s\": %s\n", 358 destPath, strerror(bytesWritten)); 359 return bytesWritten; 360 } 361 362 if (bytesWritten != bytesRead) { 363 _NotifyError(B_ERROR, "Failed to write all data to file \"%s\"\n", 364 destPath); 365 return B_ERROR; 366 } 367 368 offset += bytesRead; 369 } 370 } 371 372 373 status_t 374 BCopyEngine::_CopyAttributes(const char* sourcePath, BNode& source, 375 const char* destPath, BNode& destination) 376 { 377 char attrName[B_ATTR_NAME_LENGTH]; 378 while (source.GetNextAttrName(attrName) == B_OK) { 379 // get attr info 380 attr_info attrInfo; 381 status_t error = source.GetAttrInfo(attrName, &attrInfo); 382 if (error != B_OK) { 383 // Delay reporting/handling the error until the controller has been 384 // asked whether it is interested. 385 attrInfo.type = B_ANY_TYPE; 386 } 387 388 // filter 389 if (fController != NULL 390 && !fController->AttributeStarted(sourcePath, attrName, 391 attrInfo.type)) { 392 if (error != B_OK) { 393 _NotifyError(error, "Failed to get info of attribute \"%s\" " 394 "of file \"%s\": %s\n", attrName, sourcePath, 395 strerror(error)); 396 } 397 continue; 398 } 399 400 if (error != B_OK) { 401 error = _HandleAttributeError(sourcePath, attrName, attrInfo.type, 402 error, "Failed to get info of attribute \"%s\" of file \"%s\": " 403 "%s\n", attrName, sourcePath, strerror(error)); 404 if (error != B_OK) 405 return error; 406 continue; 407 } 408 409 // copy the attribute 410 off_t offset = 0; 411 off_t bytesLeft = attrInfo.size; 412 // go at least once through the loop, so that an empty attribute will be 413 // created as well 414 do { 415 size_t toRead = fBufferSize; 416 if ((off_t)toRead > bytesLeft) 417 toRead = bytesLeft; 418 419 // read 420 ssize_t bytesRead = source.ReadAttr(attrName, attrInfo.type, 421 offset, fBuffer, toRead); 422 if (bytesRead < 0) { 423 error = _HandleAttributeError(sourcePath, attrName, 424 attrInfo.type, bytesRead, "Failed to read attribute \"%s\" " 425 "of file \"%s\": %s\n", attrName, sourcePath, 426 strerror(bytesRead)); 427 if (error != B_OK) 428 return error; 429 break; 430 } 431 432 if (bytesRead == 0 && offset > 0) 433 break; 434 435 // write 436 ssize_t bytesWritten = destination.WriteAttr(attrName, 437 attrInfo.type, offset, fBuffer, bytesRead); 438 if (bytesWritten < 0) { 439 error = _HandleAttributeError(sourcePath, attrName, 440 attrInfo.type, bytesWritten, "Failed to write attribute " 441 "\"%s\" of file \"%s\": %s\n", attrName, destPath, 442 strerror(bytesWritten)); 443 if (error != B_OK) 444 return error; 445 break; 446 } 447 448 bytesLeft -= bytesRead; 449 offset += bytesRead; 450 } while (bytesLeft > 0); 451 452 if (fController != NULL) { 453 fController->AttributeFinished(sourcePath, attrName, attrInfo.type, 454 B_OK); 455 } 456 } 457 458 return B_OK; 459 } 460 461 462 void 463 BCopyEngine::_NotifyError(status_t error, const char* format, ...) 464 { 465 if (fController != NULL) { 466 va_list args; 467 va_start(args, format); 468 _NotifyErrorVarArgs(error, format, args); 469 va_end(args); 470 } 471 } 472 473 474 void 475 BCopyEngine::_NotifyErrorVarArgs(status_t error, const char* format, 476 va_list args) 477 { 478 if (fController != NULL) { 479 BString message; 480 message.SetToFormatVarArgs(format, args); 481 fController->ErrorOccurred(message, error); 482 } 483 } 484 485 486 status_t 487 BCopyEngine::_HandleEntryError(const char* path, status_t error, 488 const char* format, ...) 489 { 490 if (fController == NULL) 491 return error; 492 493 va_list args; 494 va_start(args, format); 495 _NotifyErrorVarArgs(error, format, args); 496 va_end(args); 497 498 if (fController->EntryFinished(path, error)) 499 return B_OK; 500 return error; 501 } 502 503 504 status_t 505 BCopyEngine::_HandleAttributeError(const char* path, const char* attribute, 506 uint32 attributeType, status_t error, const char* format, ...) 507 { 508 if (fController == NULL) 509 return error; 510 511 va_list args; 512 va_start(args, format); 513 _NotifyErrorVarArgs(error, format, args); 514 va_end(args); 515 516 if (fController->AttributeFinished(path, attribute, attributeType, error)) 517 return B_OK; 518 return error; 519 } 520 521 522 // #pragma mark - BController 523 524 525 BCopyEngine::BController::BController() 526 { 527 } 528 529 530 BCopyEngine::BController::~BController() 531 { 532 } 533 534 535 bool 536 BCopyEngine::BController::EntryStarted(const char* path) 537 { 538 return true; 539 } 540 541 542 bool 543 BCopyEngine::BController::EntryFinished(const char* path, status_t error) 544 { 545 return error == B_OK; 546 } 547 548 549 bool 550 BCopyEngine::BController::AttributeStarted(const char* path, 551 const char* attribute, uint32 attributeType) 552 { 553 return true; 554 } 555 556 557 bool 558 BCopyEngine::BController::AttributeFinished(const char* path, 559 const char* attribute, uint32 attributeType, status_t error) 560 { 561 return error == B_OK; 562 } 563 564 565 void 566 BCopyEngine::BController::ErrorOccurred(const char* message, status_t error) 567 { 568 } 569 570 571 } // namespace BPrivate 572