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
BCopyEngine(uint32 flags)37 BCopyEngine::BCopyEngine(uint32 flags)
38 :
39 fController(NULL),
40 fFlags(flags),
41 fBuffer(NULL),
42 fBufferSize(0)
43 {
44 }
45
46
~BCopyEngine()47 BCopyEngine::~BCopyEngine()
48 {
49 delete[] fBuffer;
50 }
51
52
53 BCopyEngine::BController*
Controller() const54 BCopyEngine::Controller() const
55 {
56 return fController;
57 }
58
59
60 void
SetController(BController * controller)61 BCopyEngine::SetController(BController* controller)
62 {
63 fController = controller;
64 }
65
66
67 uint32
Flags() const68 BCopyEngine::Flags() const
69 {
70 return fFlags;
71 }
72
73
74 BCopyEngine&
SetFlags(uint32 flags)75 BCopyEngine::SetFlags(uint32 flags)
76 {
77 fFlags = flags;
78 return *this;
79 }
80
81
82 BCopyEngine&
AddFlags(uint32 flags)83 BCopyEngine::AddFlags(uint32 flags)
84 {
85 fFlags |= flags;
86 return *this;
87 }
88
89
90 BCopyEngine&
RemoveFlags(uint32 flags)91 BCopyEngine::RemoveFlags(uint32 flags)
92 {
93 fFlags &= ~flags;
94 return *this;
95 }
96
97
98 status_t
CopyEntry(const Entry & sourceEntry,const Entry & destEntry)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
_CopyEntry(const char * sourcePath,const char * destPath)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[offsetof(struct dirent, d_name) + 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
_CopyFileData(const char * sourcePath,BFile & source,const char * destPath,BFile & destination)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
_CopyAttributes(const char * sourcePath,BNode & source,const char * destPath,BNode & destination)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
_NotifyError(status_t error,const char * format,...)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
_NotifyErrorVarArgs(status_t error,const char * format,va_list args)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
_HandleEntryError(const char * path,status_t error,const char * format,...)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
_HandleAttributeError(const char * path,const char * attribute,uint32 attributeType,status_t error,const char * format,...)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
BController()525 BCopyEngine::BController::BController()
526 {
527 }
528
529
~BController()530 BCopyEngine::BController::~BController()
531 {
532 }
533
534
535 bool
EntryStarted(const char * path)536 BCopyEngine::BController::EntryStarted(const char* path)
537 {
538 return true;
539 }
540
541
542 bool
EntryFinished(const char * path,status_t error)543 BCopyEngine::BController::EntryFinished(const char* path, status_t error)
544 {
545 return error == B_OK;
546 }
547
548
549 bool
AttributeStarted(const char * path,const char * attribute,uint32 attributeType)550 BCopyEngine::BController::AttributeStarted(const char* path,
551 const char* attribute, uint32 attributeType)
552 {
553 return true;
554 }
555
556
557 bool
AttributeFinished(const char * path,const char * attribute,uint32 attributeType,status_t error)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
ErrorOccurred(const char * message,status_t error)566 BCopyEngine::BController::ErrorOccurred(const char* message, status_t error)
567 {
568 }
569
570
571 } // namespace BPrivate
572