2 * Copyright 2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
10 #include <CopyEngine.h>
18 #include <Directory.h>
24 #include <TypeConstants.h>
30 static const size_t kDefaultBufferSize
= 1024 * 1024;
31 static const size_t kSmallBufferSize
= 64 * 1024;
34 // #pragma mark - BCopyEngine
37 BCopyEngine::BCopyEngine(uint32 flags
)
47 BCopyEngine::~BCopyEngine()
53 BCopyEngine::BController
*
54 BCopyEngine::Controller() const
61 BCopyEngine::SetController(BController
* controller
)
63 fController
= controller
;
68 BCopyEngine::Flags() const
75 BCopyEngine::SetFlags(uint32 flags
)
83 BCopyEngine::AddFlags(uint32 flags
)
91 BCopyEngine::RemoveFlags(uint32 flags
)
99 BCopyEngine::CopyEntry(const Entry
& sourceEntry
, const Entry
& destEntry
)
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");
109 fBufferSize
= kSmallBufferSize
;
111 fBufferSize
= kDefaultBufferSize
;
114 BPath sourcePathBuffer
;
115 const char* sourcePath
;
116 status_t error
= sourceEntry
.GetPath(sourcePathBuffer
, sourcePath
);
120 BPath destPathBuffer
;
121 const char* destPath
;
122 error
= destEntry
.GetPath(destPathBuffer
, destPath
);
126 return _CopyEntry(sourcePath
, destPath
);
131 BCopyEngine::_CopyEntry(const char* sourcePath
, const char* destPath
)
133 // apply entry filter
134 if (fController
!= NULL
&& !fController
->EntryStarted(sourcePath
))
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
));
145 struct stat destStat
;
146 bool destExists
= lstat(destPath
, &destStat
) == 0;
148 // check whether to delete/create the destination
149 bool unlinkDest
= destExists
;
150 bool createDest
= true;
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
);
160 if (S_ISDIR(sourceStat
.st_mode
)) {
161 // both are dirs; nothing to do
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
);
172 // unlink the destination
174 if (unlink(destPath
) < 0) {
175 return _HandleEntryError(sourcePath
, errno
,
176 "Failed to unlink \"%s\": %s\n", destPath
, strerror(errno
));
183 BDirectory sourceDir
;
184 BNode
* sourceNode
= NULL
;
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
;
194 error
= _sourceNode
.SetTo(sourcePath
);
195 sourceNode
= &_sourceNode
;
199 return _HandleEntryError(sourcePath
, error
,
200 "Failed to open \"%s\": %s\n", sourcePath
, strerror(error
));
203 // create the destination
207 BSymLink destSymLink
;
208 BNode
* destNode
= NULL
;
211 if (S_ISDIR(sourceStat
.st_mode
)) {
213 error
= BDirectory().CreateDirectory(destPath
, &destDir
);
215 return _HandleEntryError(sourcePath
, error
,
216 "Failed to make directory \"%s\": %s\n", destPath
,
221 } else if (S_ISREG(sourceStat
.st_mode
)) {
223 error
= BDirectory().CreateFile(destPath
, &destFile
);
225 return _HandleEntryError(sourcePath
, error
,
226 "Failed to create file \"%s\": %s\n", destPath
,
230 destNode
= &destFile
;
232 // copy file contents
233 error
= _CopyFileData(sourcePath
, sourceFile
, destPath
, destFile
);
235 if (fController
!= NULL
236 && fController
->EntryFinished(sourcePath
, error
)) {
241 } else if (S_ISLNK(sourceStat
.st_mode
)) {
243 char* linkTo
= fBuffer
;
244 ssize_t bytesRead
= readlink(sourcePath
, linkTo
, fBufferSize
- 1);
246 return _HandleEntryError(sourcePath
, errno
,
247 "Failed to read symlink \"%s\": %s\n", sourcePath
,
251 // null terminate the link contents
252 linkTo
[bytesRead
] = '\0';
255 error
= BDirectory().CreateSymLink(destPath
, linkTo
, &destSymLink
);
257 return _HandleEntryError(sourcePath
, error
,
258 "Failed to create symlink \"%s\": %s\n", destPath
,
262 destNode
= &destSymLink
;
265 return _HandleEntryError(sourcePath
, B_NOT_SUPPORTED
,
266 "Source file \"%s\" has unsupported type.\n", sourcePath
);
269 // copy attributes (before setting the permissions!)
270 error
= _CopyAttributes(sourcePath
, *sourceNode
, destPath
, *destNode
);
272 if (fController
!= NULL
273 && fController
->EntryFinished(sourcePath
, error
)) {
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
);
286 destNode
->SetModificationTime(sourceStat
.st_mtime
);
289 // the destination node is no longer needed
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) {
302 // construct new entry paths
303 BPath sourceEntryPath
;
304 error
= sourceEntryPath
.SetTo(sourcePath
, entry
->d_name
);
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
));
312 error
= destEntryPath
.SetTo(destPath
, entry
->d_name
);
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
));
320 error
= _CopyEntry(sourceEntryPath
.Path(), destEntryPath
.Path());
322 if (fController
!= NULL
323 && fController
->EntryFinished(sourcePath
, error
)) {
331 if (fController
!= NULL
)
332 fController
->EntryFinished(sourcePath
, B_OK
);
338 BCopyEngine::_CopyFileData(const char* sourcePath
, BFile
& source
,
339 const char* destPath
, BFile
& destination
)
344 ssize_t bytesRead
= source
.ReadAt(offset
, fBuffer
, fBufferSize
);
346 _NotifyError(bytesRead
, "Failed to read from file \"%s\": %s\n",
347 sourcePath
, strerror(bytesRead
));
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
));
362 if (bytesWritten
!= bytesRead
) {
363 _NotifyError(B_ERROR
, "Failed to write all data to file \"%s\"\n",
374 BCopyEngine::_CopyAttributes(const char* sourcePath
, BNode
& source
,
375 const char* destPath
, BNode
& destination
)
377 char attrName
[B_ATTR_NAME_LENGTH
];
378 while (source
.GetNextAttrName(attrName
) == B_OK
) {
381 status_t error
= source
.GetAttrInfo(attrName
, &attrInfo
);
383 // Delay reporting/handling the error until the controller has been
384 // asked whether it is interested.
385 attrInfo
.type
= B_ANY_TYPE
;
389 if (fController
!= NULL
390 && !fController
->AttributeStarted(sourcePath
, attrName
,
393 _NotifyError(error
, "Failed to get info of attribute \"%s\" "
394 "of file \"%s\": %s\n", attrName
, sourcePath
,
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
));
409 // copy the attribute
411 off_t bytesLeft
= attrInfo
.size
;
412 // go at least once through the loop, so that an empty attribute will be
415 size_t toRead
= fBufferSize
;
416 if ((off_t
)toRead
> bytesLeft
)
420 ssize_t bytesRead
= source
.ReadAttr(attrName
, attrInfo
.type
,
421 offset
, fBuffer
, toRead
);
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
));
432 if (bytesRead
== 0 && offset
> 0)
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
));
448 bytesLeft
-= bytesRead
;
450 } while (bytesLeft
> 0);
452 if (fController
!= NULL
) {
453 fController
->AttributeFinished(sourcePath
, attrName
, attrInfo
.type
,
463 BCopyEngine::_NotifyError(status_t error
, const char* format
, ...)
465 if (fController
!= NULL
) {
467 va_start(args
, format
);
468 _NotifyErrorVarArgs(error
, format
, args
);
475 BCopyEngine::_NotifyErrorVarArgs(status_t error
, const char* format
,
478 if (fController
!= NULL
) {
480 message
.SetToFormatVarArgs(format
, args
);
481 fController
->ErrorOccurred(message
, error
);
487 BCopyEngine::_HandleEntryError(const char* path
, status_t error
,
488 const char* format
, ...)
490 if (fController
== NULL
)
494 va_start(args
, format
);
495 _NotifyErrorVarArgs(error
, format
, args
);
498 if (fController
->EntryFinished(path
, error
))
505 BCopyEngine::_HandleAttributeError(const char* path
, const char* attribute
,
506 uint32 attributeType
, status_t error
, const char* format
, ...)
508 if (fController
== NULL
)
512 va_start(args
, format
);
513 _NotifyErrorVarArgs(error
, format
, args
);
516 if (fController
->AttributeFinished(path
, attribute
, attributeType
, error
))
522 // #pragma mark - BController
525 BCopyEngine::BController::BController()
530 BCopyEngine::BController::~BController()
536 BCopyEngine::BController::EntryStarted(const char* path
)
543 BCopyEngine::BController::EntryFinished(const char* path
, status_t error
)
545 return error
== B_OK
;
550 BCopyEngine::BController::AttributeStarted(const char* path
,
551 const char* attribute
, uint32 attributeType
)
558 BCopyEngine::BController::AttributeFinished(const char* path
,
559 const char* attribute
, uint32 attributeType
, status_t error
)
561 return error
== B_OK
;
566 BCopyEngine::BController::ErrorOccurred(const char* message
, status_t error
)
571 } // namespace BPrivate