From f76e944e24b1686fc3c45c30a47de543c025f11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 14 Jun 2009 11:07:52 +0000 Subject: [PATCH] * Fixed problems when installing onto non-empty target volumes. The 'system' folder will be a clean copy of the source volume. Other folders will be merged (as before), but in case a folder is in the way of a link or file from the source volume, it will now be purged. * Clarified the alert for non-empty target volumes, so it is very clear what happens. (Maybe there ought to the be option to only copy the system folder, though.) * Fixed a problem with copying attributes in certain cases. * Fixed the main GUI not resetting state properly after encountering an error during the copy process. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31042 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/installer/CopyEngine.cpp | 132 +++++++++++++++++++------ src/apps/installer/CopyEngine.h | 6 ++ src/apps/installer/InstallerWindow.cpp | 5 +- src/apps/installer/WorkerThread.cpp | 15 ++- 4 files changed, 121 insertions(+), 37 deletions(-) diff --git a/src/apps/installer/CopyEngine.cpp b/src/apps/installer/CopyEngine.cpp index 338c1f692d..9894ae926b 100644 --- a/src/apps/installer/CopyEngine.cpp +++ b/src/apps/installer/CopyEngine.cpp @@ -263,8 +263,11 @@ CopyEngine::_CopyFolder(const char* _source, const char* _destination, return ret; ret = create_directory(_destination, 0777); - if (ret < B_OK && ret != B_FILE_EXISTS) + if (ret < B_OK && ret != B_FILE_EXISTS) { + fprintf(stderr, "Could not create '%s': %s\n", _destination, + strerror(ret)); return ret; + } BDirectory destination(_destination); ret = destination.InitCheck(); @@ -301,10 +304,24 @@ CopyEngine::_CopyFolder(const char* _source, const char* _destination, // handle recursive directory copy if (copy.Exists()) { - // Do not overwrite attributes on folders that exist. - // This should work better when the install target already - // contains a Haiku installation. - copyAttributes = false; + ret = B_OK; + if (copy.IsDirectory()) { + if (_ShouldClobberFolder(name, 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 srcFolder; @@ -329,35 +346,52 @@ CopyEngine::_CopyFolder(const char* _source, const char* _destination, // We are supposed to quit return B_CANCELED; } - } else 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 (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) + return ret; + // copy attributes BNode sourceNode(&entry); BNode targetNode(©); @@ -396,6 +430,30 @@ CopyEngine::_CopyFolder(const char* _source, const char* _destination, } +status_t +CopyEngine::_RemoveFolder(BEntry& entry) +{ + BDirectory directory(&entry); + status_t ret = directory.InitCheck(); + if (ret != B_OK) + return ret; + + BEntry subEntry; + while (directory.GetNextEntry(&subEntry) == B_OK) { + if (subEntry.IsDirectory()) { + ret = _RemoveFolder(subEntry); + if (ret != B_OK) + return ret; + } else { + ret = subEntry.Remove(); + if (ret != B_OK) + return ret; + } + } + return entry.Remove(); +} + + void CopyEngine::_UpdateProgress() { @@ -430,6 +488,24 @@ CopyEngine::_ShouldCopyEntry(const char* name, const struct stat& statInfo, } +bool +CopyEngine::_ShouldClobberFolder(const char* name, const struct stat& statInfo, + int32 level) const +{ + if (level == 1 && S_ISDIR(statInfo.st_mode)) { + if (strcmp("system", name) == 0) { + printf("clobbering '%s'.\n", name); + return true; + } +// if (strcmp("develop", name) == 0) { +// printf("clobbering '%s'.\n", name); +// return true; +// } + } + return false; +} + + int32 CopyEngine::_WriteThreadEntry(void* cookie) { diff --git a/src/apps/installer/CopyEngine.h b/src/apps/installer/CopyEngine.h index f34560850c..1ad12b44ce 100644 --- a/src/apps/installer/CopyEngine.h +++ b/src/apps/installer/CopyEngine.h @@ -48,6 +48,12 @@ private: const struct stat& statInfo, int32 level) const; + bool _ShouldClobberFolder(const char* name, + const struct stat& statInfo, + int32 level) const; + + status_t _RemoveFolder(BEntry& entry); + void _UpdateProgress(); static int32 _WriteThreadEntry(void* cookie); diff --git a/src/apps/installer/InstallerWindow.cpp b/src/apps/installer/InstallerWindow.cpp index 1f9dd56a05..89d0cfd179 100644 --- a/src/apps/installer/InstallerWindow.cpp +++ b/src/apps/installer/InstallerWindow.cpp @@ -373,15 +373,12 @@ InstallerWindow::MessageReceived(BMessage *msg) (new BAlert("error", errorMessage, "Ok"))->Go(); } - fInstallStatus = kReadyForInstall; - fBeginButton->SetEnabled(true); _DisableInterface(false); fProgressLayoutItem->SetVisible(false); fPkgSwitchLayoutItem->SetVisible(true); _ShowOptionalPackages(); - - fBeginButton->SetLabel("Begin"); + _UpdateControls(); break; } case START_SCAN: diff --git a/src/apps/installer/WorkerThread.cpp b/src/apps/installer/WorkerThread.cpp index cb818e5bf5..b4f0b3a084 100644 --- a/src/apps/installer/WorkerThread.cpp +++ b/src/apps/installer/WorkerThread.cpp @@ -234,14 +234,14 @@ WorkerThread::_LaunchFinishScript(BPath &path) void -WorkerThread::_PerformInstall(BMenu *srcMenu, BMenu *targetMenu) +WorkerThread::_PerformInstall(BMenu* srcMenu, BMenu* targetMenu) { CALLED(); BPath targetDirectory, srcDirectory; BDirectory targetDir; BDiskDevice device; - BPartition *partition; + BPartition* partition; BVolume targetVolume; status_t err = B_OK; int32 entries = 0; @@ -250,8 +250,8 @@ WorkerThread::_PerformInstall(BMenu *srcMenu, BMenu *targetMenu) BMessenger messenger(fWindow); CopyEngine engine(messenger, new BMessage(MSG_STATUS_MESSAGE)); - PartitionMenuItem *targetItem = (PartitionMenuItem *)targetMenu->FindMarked(); - PartitionMenuItem *srcItem = (PartitionMenuItem *)srcMenu->FindMarked(); + PartitionMenuItem* targetItem = (PartitionMenuItem*)targetMenu->FindMarked(); + PartitionMenuItem* srcItem = (PartitionMenuItem*)srcMenu->FindMarked(); if (!srcItem || !targetItem) { ERR("bad menu items\n"); goto error; @@ -381,7 +381,12 @@ WorkerThread::_PerformInstall(BMenu *srcMenu, BMenu *targetMenu) } if (entries != 0 && ((new BAlert("", "The target volume is not empty. Are you sure you " - "want to install anyway?", "Install Anyway", "Cancel", 0, + "want to install anyway?\n\nNote: The 'system' folder will be a " + "clean copy from the source volume, all other folders will be " + "merged, whereas files and links that exist on both the source " + "and target volume will be overwritten with the source volume " + "version.", + "Install Anyway", "Cancel", 0, B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) { err = B_CANCELED; goto error;