diff --git a/src/apps/installer/CopyEngine.cpp b/src/apps/installer/CopyEngine.cpp index e2dab52d49..646f6173db 100644 --- a/src/apps/installer/CopyEngine.cpp +++ b/src/apps/installer/CopyEngine.cpp @@ -143,6 +143,30 @@ CopyEngine::Copy(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 = CopyEngine::RemoveFolder(subEntry); + if (ret != B_OK) + return ret; + } else { + ret = subEntry.Remove(); + if (ret != B_OK) + return ret; + } + } + return entry.Remove(); +} + + status_t CopyEngine::_CopyData(const BEntry& _source, const BEntry& _destination, sem_id cancelSemaphore) @@ -335,16 +359,10 @@ CopyEngine::_Copy(BEntry &source, BEntry &destination, if (destination.Exists()) { if (destination.IsDirectory()) { - if (fEntryFilter - && fEntryFilter->ShouldClobberFolder(source, - relativeSourcePath, sourceInfo)) { - 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. - copyAttributesToTarget = false; - } + // Do not overwrite attributes on folders that exist. + // This should work better when the install target + // already contains a Haiku installation. + copyAttributesToTarget = false; } else { ret = destination.Remove(); } @@ -354,13 +372,15 @@ CopyEngine::_Copy(BEntry &source, BEntry &destination, "%s\n", sourcePath.Path(), strerror(ret)); 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; - } + } + + ret = create_directory(destPath.Path(), 0777); + // Make sure the target path exists, it may have been deleted if + // the existing destination was a file instead of a directory. + 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); @@ -381,7 +401,7 @@ CopyEngine::_Copy(BEntry &source, BEntry &destination, } else { if (destination.Exists()) { if (destination.IsDirectory()) - ret = _RemoveFolder(destination); + ret = CopyEngine::RemoveFolder(destination); else ret = destination.Remove(); if (ret != B_OK) { @@ -466,30 +486,6 @@ CopyEngine::_Copy(BEntry &source, BEntry &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(); -} - - const char* CopyEngine::_RelativeEntryPath(const char* absoluteSourcePath) const { diff --git a/src/apps/installer/CopyEngine.h b/src/apps/installer/CopyEngine.h index 2663ccbeed..21226a43fd 100644 --- a/src/apps/installer/CopyEngine.h +++ b/src/apps/installer/CopyEngine.h @@ -37,6 +37,8 @@ public: sem_id cancelSemaphore = -1, bool copyAttributes = true); + static status_t RemoveFolder(BEntry& entry); + private: status_t _CollectCopyInfo(const char* source, sem_id cancelSemaphore, off_t& bytesToCopy, @@ -48,8 +50,6 @@ private: const BEntry& destination, sem_id cancelSemaphore = -1); - status_t _RemoveFolder(BEntry& entry); - const char* _RelativeEntryPath( const char* absoluteSourcePath) const; @@ -118,9 +118,6 @@ public: virtual bool ShouldCopyEntry(const BEntry& entry, const char* path, const struct stat& statInfo) const = 0; - virtual bool ShouldClobberFolder(const BEntry& entry, - const char* path, - const struct stat& statInfo) const = 0; }; diff --git a/src/apps/installer/WorkerThread.cpp b/src/apps/installer/WorkerThread.cpp index 1fd607ea75..e301073d06 100644 --- a/src/apps/installer/WorkerThread.cpp +++ b/src/apps/installer/WorkerThread.cpp @@ -137,18 +137,6 @@ public: return true; } - virtual bool ShouldClobberFolder(const BEntry& entry, const char* path, - const struct stat& statInfo) const - { - if (S_ISDIR(statInfo.st_mode) && strncmp("system/", path, 7) == 0 - && strcmp("system/settings", path) != 0) { - // Replace everything in "system" besides "settings" - printf("clobbering '%s'.\n", path); - return true; - } - return false; - } - private: typedef std::set StringSet; @@ -460,12 +448,11 @@ WorkerThread::_PerformInstall(partition_id sourcePartitionID, if (entries != 0) { BAlert* alert = new BAlert("", B_TRANSLATE("The target volume is not " - "empty. Are you sure you want to install anyway?\n\nNote: The " - "'system' folder will be a clean copy from the source volume while " - "the existing 'settings' folder is retained. All other folders " - "will be merged, in which files and links that exist on both the " - "source and target volume will be overwritten with the source " - "volume version."), + "empty. If it already contains a Haiku installation, it will be " + "overwritten. This will remove all installed software.\n\n" + "If you want to upgrade your system without removing installed " + "software, see the Haiku user guide for update instructions.\n\n" + "Are you sure you want to continue the installation?"), B_TRANSLATE("Install anyway"), B_TRANSLATE("Cancel"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->SetShortcut(1, B_ESCAPE); @@ -474,6 +461,9 @@ WorkerThread::_PerformInstall(partition_id sourcePartitionID, // folders at the user's choice. return _InstallationError(B_CANCELED); } + err = _PrepareCleanInstall(targetDirectory); + if (err != B_OK) + return _InstallationError(err); } // Begin actual installation @@ -582,6 +572,57 @@ WorkerThread::_PerformInstall(partition_id sourcePartitionID, } +status_t +WorkerThread::_PrepareCleanInstall(const BPath& targetDirectory) const +{ + // When a target volume has files (other than the trash), the /system + // folder will be purged, except for the /system/settings subdirectory. + BPath systemPath(targetDirectory.Path(), "system", true); + status_t ret = systemPath.InitCheck(); + if (ret != B_OK) + return ret; + + BEntry systemEntry(systemPath.Path()); + ret = systemEntry.InitCheck(); + if (ret != B_OK) + return ret; + if (!systemEntry.Exists()) + // target does not exist, done + return B_OK; + if (!systemEntry.IsDirectory()) + // the system entry is a file or a symlink + return systemEntry.Remove(); + + BDirectory systemDirectory(&systemEntry); + ret = systemDirectory.InitCheck(); + if (ret != B_OK) + return ret; + + BEntry subEntry; + char fileName[B_FILE_NAME_LENGTH]; + while (systemDirectory.GetNextEntry(&subEntry) == B_OK) { + ret = subEntry.GetName(fileName); + if (ret != B_OK) + return ret; + + if (subEntry.IsDirectory() && strcmp(fileName, "settings") == 0) { + // Keep the settings folder + continue; + } else if (subEntry.IsDirectory()) { + ret = CopyEngine::RemoveFolder(subEntry); + if (ret != B_OK) + return ret; + } else { + ret = subEntry.Remove(); + if (ret != B_OK) + return ret; + } + } + + return B_OK; +} + + status_t WorkerThread::_InstallationError(status_t error) { diff --git a/src/apps/installer/WorkerThread.h b/src/apps/installer/WorkerThread.h index 9a587cfcdd..d2e3ce3a85 100644 --- a/src/apps/installer/WorkerThread.h +++ b/src/apps/installer/WorkerThread.h @@ -44,6 +44,8 @@ private: status_t _PerformInstall(partition_id sourcePartitionID, partition_id targetPartitionID); + status_t _PrepareCleanInstall( + const BPath& targetDirectory) const; status_t _InstallationError(status_t error); status_t _MirrorIndices(const BPath& srcDirectory, const BPath& targetDirectory) const;