installer/CopyEngine: now only exposes a Copy() method
Previously this class exposes two methods: - CopyFile(): only copy the file data, nothing else. - CopyFolder(): copy files & directories between two folders, while preserving the attributes as well as symlinks. With this commit, everything is unified into one method: Copy(). This method can handle files, directories, symlinks and optionally also copy attributes. Since most of the logic was just moved around, we can be rather certain that this won't disrupt CopyEngine behaviors by much. In the future we should look into using BCopyEngine to replace the copying part of CopyEngine, as they seems to be compatible. This change allows the Installer to make use of CopyEngine as a general-purpose copier, in preparation for optional .hpkg installation support. Change-Id: Iad5ba2ebc9f34b7822e550b415308fd2b43eed47 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2399 Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
parent
fd09d551e1
commit
bf551d3889
@ -128,16 +128,28 @@ CopyEngine::CollectTargets(const char* source, sem_id cancelSemaphore)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
CopyEngine::CopyFolder(const char* source, const char* destination,
|
CopyEngine::Copy(const char* _source, const char* _destination,
|
||||||
sem_id cancelSemaphore)
|
sem_id cancelSemaphore, bool copyAttributes)
|
||||||
{
|
{
|
||||||
int32 level = 0;
|
int32 level = 0;
|
||||||
return _CopyFolder(source, destination, level, cancelSemaphore);
|
status_t ret;
|
||||||
|
|
||||||
|
BEntry source(_source);
|
||||||
|
ret = source.InitCheck();
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
BEntry destination(_destination);
|
||||||
|
ret = destination.InitCheck();
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return _Copy(source, destination, level, cancelSemaphore, copyAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
CopyEngine::CopyFile(const BEntry& _source, const BEntry& _destination,
|
CopyEngine::_CopyData(const BEntry& _source, const BEntry& _destination,
|
||||||
sem_id cancelSemaphore)
|
sem_id cancelSemaphore)
|
||||||
{
|
{
|
||||||
SemaphoreLocker lock(cancelSemaphore);
|
SemaphoreLocker lock(cancelSemaphore);
|
||||||
@ -282,157 +294,153 @@ CopyEngine::_CollectCopyInfo(const char* _source, int32& level,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
CopyEngine::_CopyFolder(const char* _source, const char* _destination,
|
CopyEngine::_Copy(BEntry &source, BEntry &destination,
|
||||||
int32& level, sem_id cancelSemaphore)
|
int32& level, sem_id cancelSemaphore, bool copyAttributes)
|
||||||
{
|
{
|
||||||
level++;
|
level++;
|
||||||
|
|
||||||
BDirectory source(_source);
|
struct stat sourceInfo;
|
||||||
status_t ret = source.InitCheck();
|
status_t ret = source.GetStat(&sourceInfo);
|
||||||
if (ret < B_OK)
|
if (ret != B_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = create_directory(_destination, 0777);
|
SemaphoreLocker lock(cancelSemaphore);
|
||||||
if (ret < B_OK && ret != B_FILE_EXISTS) {
|
if (cancelSemaphore >= 0 && !lock.IsLocked()) {
|
||||||
fprintf(stderr, "Could not create '%s': %s\n", _destination,
|
// We are supposed to quit
|
||||||
strerror(ret));
|
return B_CANCELED;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BDirectory destination(_destination);
|
BPath sourcePath(&source);
|
||||||
ret = destination.InitCheck();
|
ret = sourcePath.InitCheck();
|
||||||
if (ret < B_OK)
|
if (ret != B_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
BEntry entry;
|
BPath destPath(&destination);
|
||||||
while (source.GetNextEntry(&entry) == B_OK) {
|
ret = destPath.InitCheck();
|
||||||
SemaphoreLocker lock(cancelSemaphore);
|
if (ret != B_OK)
|
||||||
if (cancelSemaphore >= 0 && !lock.IsLocked()) {
|
return ret;
|
||||||
// We are supposed to quit
|
|
||||||
return B_CANCELED;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* name = entry.Name();
|
const char *relativeSourcePath = _RelativeEntryPath(sourcePath.Path());
|
||||||
BPath sourceEntryPath;
|
if (fEntryFilter != NULL
|
||||||
status_t ret = entry.GetPath(&sourceEntryPath);
|
&& !fEntryFilter->ShouldCopyEntry(source, relativeSourcePath,
|
||||||
|
sourceInfo, level)) {
|
||||||
|
// Silently skip the filtered entry.
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancelSemaphore >= 0)
|
||||||
|
lock.Unlock();
|
||||||
|
|
||||||
|
if (S_ISDIR(sourceInfo.st_mode)) {
|
||||||
|
BDirectory sourceDirectory(&source);
|
||||||
|
ret = sourceDirectory.InitCheck();
|
||||||
if (ret != B_OK)
|
if (ret != B_OK)
|
||||||
return ret;
|
return ret;
|
||||||
const char* relativeSourceEntryPath
|
|
||||||
= _RelativeEntryPath(sourceEntryPath.Path());
|
|
||||||
|
|
||||||
struct stat statInfo;
|
if (destination.Exists()) {
|
||||||
entry.GetStat(&statInfo);
|
if (destination.IsDirectory()) {
|
||||||
|
if (fEntryFilter
|
||||||
|
&& fEntryFilter->ShouldClobberFolder(source,
|
||||||
|
relativeSourcePath, sourceInfo, level)) {
|
||||||
|
ret = _RemoveFolder(destination);
|
||||||
|
} else {
|
||||||
|
// Do not overwrite attributes on folders that exist.
|
||||||
|
// This should work better when the install target
|
||||||
|
// already contains a Haiku installation.
|
||||||
|
copyAttributes = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = destination.Remove();
|
||||||
|
}
|
||||||
|
|
||||||
if (fEntryFilter != NULL
|
if (ret != B_OK) {
|
||||||
&& !fEntryFilter->ShouldCopyEntry(entry, relativeSourceEntryPath,
|
fprintf(stderr, "Failed to make room for folder '%s': "
|
||||||
statInfo, level)) {
|
"%s\n", sourcePath.Path(), strerror(ret));
|
||||||
continue;
|
return ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = create_directory(destPath.Path(), 0777);
|
||||||
|
if (ret != B_OK && ret != B_FILE_EXISTS) {
|
||||||
|
fprintf(stderr, "Could not create '%s': %s\n", destPath.Path(),
|
||||||
|
strerror(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BDirectory destDirectory(&destination);
|
||||||
|
ret = destDirectory.InitCheck();
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
BEntry entry;
|
||||||
|
while (sourceDirectory.GetNextEntry(&entry) == B_OK) {
|
||||||
|
BEntry dest(&destDirectory, entry.Name());
|
||||||
|
ret = dest.InitCheck();
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
ret = _Copy(entry, dest, level,
|
||||||
|
cancelSemaphore, copyAttributes);
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (destination.Exists()) {
|
||||||
|
if (destination.IsDirectory())
|
||||||
|
ret = _RemoveFolder(destination);
|
||||||
|
else
|
||||||
|
ret = destination.Remove();
|
||||||
|
if (ret != B_OK) {
|
||||||
|
fprintf(stderr, "Failed to make room for entry '%s': "
|
||||||
|
"%s\n", sourcePath.Path(), strerror(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fItemsCopied++;
|
fItemsCopied++;
|
||||||
fCurrentItem = name;
|
BPath destDirectory;
|
||||||
fCurrentTargetFolder = _destination;
|
ret = destPath.GetParent(&destDirectory);
|
||||||
_UpdateProgress();
|
|
||||||
|
|
||||||
BEntry copy(&destination, name);
|
|
||||||
bool copyAttributes = true;
|
|
||||||
|
|
||||||
if (S_ISDIR(statInfo.st_mode)) {
|
|
||||||
// handle recursive directory copy
|
|
||||||
|
|
||||||
if (copy.Exists()) {
|
|
||||||
ret = B_OK;
|
|
||||||
if (copy.IsDirectory()) {
|
|
||||||
if (fEntryFilter
|
|
||||||
&& fEntryFilter->ShouldClobberFolder(entry,
|
|
||||||
relativeSourceEntryPath, statInfo, level)) {
|
|
||||||
ret = _RemoveFolder(copy);
|
|
||||||
} else {
|
|
||||||
// Do not overwrite attributes on folders that exist.
|
|
||||||
// This should work better when the install target
|
|
||||||
// already contains a Haiku installation.
|
|
||||||
copyAttributes = false;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
ret = copy.Remove();
|
|
||||||
|
|
||||||
if (ret != B_OK) {
|
|
||||||
fprintf(stderr, "Failed to make room for folder '%s': "
|
|
||||||
"%s\n", name, strerror(ret));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BPath dstFolder;
|
|
||||||
ret = copy.GetPath(&dstFolder);
|
|
||||||
if (ret < B_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (cancelSemaphore >= 0)
|
|
||||||
lock.Unlock();
|
|
||||||
|
|
||||||
ret = _CopyFolder(sourceEntryPath.Path(), dstFolder.Path(), level,
|
|
||||||
cancelSemaphore);
|
|
||||||
if (ret < B_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (cancelSemaphore >= 0 && !lock.Lock()) {
|
|
||||||
// We are supposed to quit
|
|
||||||
return B_CANCELED;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (copy.Exists()) {
|
|
||||||
if (copy.IsDirectory())
|
|
||||||
ret = _RemoveFolder(copy);
|
|
||||||
else
|
|
||||||
ret = copy.Remove();
|
|
||||||
if (ret != B_OK) {
|
|
||||||
fprintf(stderr, "Failed to make room for entry '%s': "
|
|
||||||
"%s\n", name, strerror(ret));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (S_ISLNK(statInfo.st_mode)) {
|
|
||||||
// copy symbolic links
|
|
||||||
BSymLink srcLink(&entry);
|
|
||||||
if (ret < B_OK)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
char linkPath[B_PATH_NAME_LENGTH];
|
|
||||||
ssize_t read = srcLink.ReadLink(linkPath, B_PATH_NAME_LENGTH - 1);
|
|
||||||
if (read < 0)
|
|
||||||
return (status_t)read;
|
|
||||||
|
|
||||||
// just in case it already exists...
|
|
||||||
copy.Remove();
|
|
||||||
|
|
||||||
BSymLink dstLink;
|
|
||||||
ret = destination.CreateSymLink(name, linkPath, &dstLink);
|
|
||||||
if (ret < B_OK)
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
// copy file data
|
|
||||||
// NOTE: Do not pass the locker, we simply keep holding the lock!
|
|
||||||
ret = CopyFile(entry, copy);
|
|
||||||
if (ret < B_OK)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!copyAttributes)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ret = copy.SetTo(&destination, name);
|
|
||||||
if (ret != B_OK)
|
if (ret != B_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
fCurrentTargetFolder = destDirectory.Path();
|
||||||
|
fCurrentItem = sourcePath.Leaf();
|
||||||
|
_UpdateProgress();
|
||||||
|
|
||||||
|
if (S_ISLNK(sourceInfo.st_mode)) {
|
||||||
|
// copy symbolic links
|
||||||
|
BSymLink srcLink(&source);
|
||||||
|
ret = srcLink.InitCheck();
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
char linkPath[B_PATH_NAME_LENGTH];
|
||||||
|
ssize_t read = srcLink.ReadLink(linkPath, B_PATH_NAME_LENGTH - 1);
|
||||||
|
if (read < 0)
|
||||||
|
return (status_t)read;
|
||||||
|
|
||||||
|
BDirectory dstFolder;
|
||||||
|
ret = destination.GetParent(&dstFolder);
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
ret = dstFolder.CreateSymLink(sourcePath.Leaf(), linkPath, NULL);
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
// copy file data
|
||||||
|
// NOTE: Do not pass the locker, we simply keep holding the lock!
|
||||||
|
ret = _CopyData(source, destination);
|
||||||
|
if (ret != B_OK)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copyAttributes) {
|
||||||
// copy attributes
|
// copy attributes
|
||||||
BNode sourceNode(&entry);
|
BNode sourceNode(&source);
|
||||||
BNode targetNode(©);
|
BNode targetNode(&destination);
|
||||||
char attrName[B_ATTR_NAME_LENGTH];
|
char attrName[B_ATTR_NAME_LENGTH];
|
||||||
while (sourceNode.GetNextAttrName(attrName) == B_OK) {
|
while (sourceNode.GetNextAttrName(attrName) == B_OK) {
|
||||||
attr_info info;
|
attr_info info;
|
||||||
if (sourceNode.GetAttrInfo(attrName, &info) < B_OK)
|
if (sourceNode.GetAttrInfo(attrName, &info) != B_OK)
|
||||||
continue;
|
continue;
|
||||||
size_t size = 4096;
|
size_t size = 4096;
|
||||||
uint8 buffer[size];
|
uint8 buffer[size];
|
||||||
@ -452,11 +460,11 @@ CopyEngine::_CopyFolder(const char* _source, const char* _destination,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy basic attributes
|
// copy basic attributes
|
||||||
copy.SetPermissions(statInfo.st_mode);
|
destination.SetPermissions(sourceInfo.st_mode);
|
||||||
copy.SetOwner(statInfo.st_uid);
|
destination.SetOwner(sourceInfo.st_uid);
|
||||||
copy.SetGroup(statInfo.st_gid);
|
destination.SetGroup(sourceInfo.st_gid);
|
||||||
copy.SetModificationTime(statInfo.st_mtime);
|
destination.SetModificationTime(sourceInfo.st_mtime);
|
||||||
copy.SetCreationTime(statInfo.st_crtime);
|
destination.SetCreationTime(sourceInfo.st_crtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
level--;
|
level--;
|
||||||
|
@ -32,20 +32,21 @@ public:
|
|||||||
status_t CollectTargets(const char* source,
|
status_t CollectTargets(const char* source,
|
||||||
sem_id cancelSemaphore = -1);
|
sem_id cancelSemaphore = -1);
|
||||||
|
|
||||||
status_t CopyFolder(const char* source,
|
status_t Copy(const char* source,
|
||||||
const char* destination,
|
const char* destination,
|
||||||
sem_id cancelSemaphore = -1);
|
sem_id cancelSemaphore = -1,
|
||||||
|
bool copyAttributes = true);
|
||||||
status_t CopyFile(const BEntry& entry,
|
|
||||||
const BEntry& destination,
|
|
||||||
sem_id cancelSemaphore = -1);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _CollectCopyInfo(const char* source,
|
status_t _CollectCopyInfo(const char* source,
|
||||||
int32& level, sem_id cancelSemaphore);
|
int32& level, sem_id cancelSemaphore);
|
||||||
status_t _CopyFolder(const char* source,
|
status_t _Copy(BEntry& source,
|
||||||
const char* destination,
|
BEntry& destination,
|
||||||
int32& level, sem_id cancelSemaphore);
|
int32& level, sem_id cancelSemaphore,
|
||||||
|
bool copyAttributes);
|
||||||
|
status_t _CopyData(const BEntry& entry,
|
||||||
|
const BEntry& destination,
|
||||||
|
sem_id cancelSemaphore = -1);
|
||||||
|
|
||||||
status_t _RemoveFolder(BEntry& entry);
|
status_t _RemoveFolder(BEntry& entry);
|
||||||
|
|
||||||
|
@ -534,7 +534,7 @@ WorkerThread::_PerformInstall(partition_id sourcePartitionID,
|
|||||||
reporter.StartTimer();
|
reporter.StartTimer();
|
||||||
|
|
||||||
// copy source volume
|
// copy source volume
|
||||||
err = engine.CopyFolder(srcDirectory.Path(), targetDirectory.Path(),
|
err = engine.Copy(srcDirectory.Path(), targetDirectory.Path(),
|
||||||
fCancelSemaphore);
|
fCancelSemaphore);
|
||||||
if (err != B_OK)
|
if (err != B_OK)
|
||||||
return _InstallationError(err);
|
return _InstallationError(err);
|
||||||
@ -546,7 +546,7 @@ WorkerThread::_PerformInstall(partition_id sourcePartitionID,
|
|||||||
for (int32 i = 0; i < count; i++) {
|
for (int32 i = 0; i < count; i++) {
|
||||||
Package *p = static_cast<Package*>(fPackages->ItemAt(i));
|
Package *p = static_cast<Package*>(fPackages->ItemAt(i));
|
||||||
BPath packageDir(pkgRootDir.Path(), p->Folder());
|
BPath packageDir(pkgRootDir.Path(), p->Folder());
|
||||||
err = engine.CopyFolder(packageDir.Path(), targetDirectory.Path(),
|
err = engine.Copy(packageDir.Path(), targetDirectory.Path(),
|
||||||
fCancelSemaphore);
|
fCancelSemaphore);
|
||||||
if (err != B_OK)
|
if (err != B_OK)
|
||||||
return _InstallationError(err);
|
return _InstallationError(err);
|
||||||
|
Loading…
Reference in New Issue
Block a user