Skip to content

Commit 90c2677

Browse files
committed
Add extra error checking and retries when extracting filesystem
1 parent b5420db commit 90c2677

File tree

4 files changed

+80
-36
lines changed

4 files changed

+80
-36
lines changed

Source/PluginProcessor.cpp

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ PluginProcessor::PluginProcessor()
106106
// Initialise directory structure and settings file, do this only once if we're inside the DAW
107107
static bool filesystemInitialised = false;
108108
if(!filesystemInitialised) {
109-
initialiseFilesystem();
109+
auto succeeded = initialiseFilesystem();
110+
if(!succeeded)
111+
{
112+
logError("Failed to initialise filesystem. Is the disk full?");
113+
}
110114
filesystemInitialised = true;
111115
}
112116
settingsFile = SettingsFile::getInstance()->initialise();
@@ -219,7 +223,7 @@ void PluginProcessor::doubleFlushMessageQueue()
219223
unlockAudioThread();
220224
}
221225

222-
void PluginProcessor::initialiseFilesystem()
226+
bool PluginProcessor::initialiseFilesystem()
223227
{
224228
auto const& homeDir = ProjectInfo::appDataDir;
225229
auto const& versionDataDir = ProjectInfo::versionDataDir;
@@ -249,9 +253,11 @@ void PluginProcessor::initialiseFilesystem()
249253

250254
initMutex.create();
251255

256+
bool extractionCompleted = true;
257+
252258
// Check if the abstractions directory exists, if not, unzip it from binaryData
253259
if (!versionDataDir.exists()) {
254-
260+
extractionCompleted = false;
255261
// Binary data shouldn't be too big, then the compiler will run out of memory
256262
// To prevent this, we split the binarydata into multiple files, and add them back together here
257263
HeapArray<uint8_t> allData;
@@ -269,13 +275,30 @@ void PluginProcessor::initialiseFilesystem()
269275
}
270276

271277
versionDataDir.getParentDirectory().createDirectory();
272-
auto const tempVersionDataDir = versionDataDir.getParentDirectory().getChildFile("plugdata_version");
273-
274-
versionDataDir.getParentDirectory().createDirectory();
275-
Decompress::extractTarXz(allData.data(), allData.size(), tempVersionDataDir.getParentDirectory(), 40 * 1024 * 1024);
278+
int maxRetries = 3;
279+
int retryCount = 0;
276280

277-
// Create filesystem for this specific version
278-
tempVersionDataDir.moveFileTo(versionDataDir);
281+
while(!extractionCompleted && retryCount < maxRetries) {
282+
auto const tempVersionDataDir = versionDataDir.getParentDirectory().getChildFile("plugdata_version");
283+
284+
tempVersionDataDir.deleteRecursively();
285+
286+
if (!versionDataDir.getParentDirectory().createDirectory()) {
287+
retryCount++;
288+
continue;
289+
}
290+
291+
if (!Decompress::extractTarXz(allData.data(), allData.size(), tempVersionDataDir.getParentDirectory(), 40 * 1024 * 1024)) {
292+
retryCount++;
293+
continue;
294+
}
295+
296+
extractionCompleted = tempVersionDataDir.moveFileTo(versionDataDir);
297+
if (!extractionCompleted) {
298+
retryCount++;
299+
Thread::sleep(100);
300+
}
301+
}
279302
}
280303
if (!dekenDir.exists()) {
281304
dekenDir.createDirectory();
@@ -296,12 +319,27 @@ void PluginProcessor::initialiseFilesystem()
296319

297320
File(versionDataDir.getChildFile("./Documentation/7.stuff/tools/testtone.pd")).copyFileTo(testTonePatch);
298321
File(versionDataDir.getChildFile("./Documentation/7.stuff/tools/load-meter.pd")).copyFileTo(cpuTestPatch);
299-
300-
// We want to recreate these symlinks so that they link to the abstractions/docs for the current plugdata version
301-
homeDir.getChildFile("Abstractions").deleteFile();
302-
homeDir.getChildFile("Documentation").deleteFile();
303-
homeDir.getChildFile("Extra").deleteFile();
304-
322+
323+
auto createLinkWithRetry = [&extractionCompleted](const File& linkPath, const File& targetPath, int maxRetries = 3) {
324+
for (int retry = 0; retry < maxRetries; retry++) {
325+
#if JUCE_WINDOWS
326+
// Clean up existing link/directory
327+
if (linkPath.exists()) {
328+
linkPath.deleteRecursively();
329+
}
330+
331+
if(OSUtils::createJunction(linkPath.getFullPathName().replaceCharacters("/", "\\").toStdString(), targetPath.getFullPathName().toStdString())) return;
332+
#else
333+
if(targetPath.createSymbolicLink(linkPath, true)) return;
334+
#endif
335+
336+
if (retry < maxRetries - 1) {
337+
Thread::sleep(100);
338+
}
339+
}
340+
extractionCompleted = false;
341+
};
342+
305343
// We always want to update the symlinks in case an older version of plugdata was used
306344
#if JUCE_WINDOWS
307345
// Get paths that need symlinks
@@ -310,11 +348,10 @@ void PluginProcessor::initialiseFilesystem()
310348
auto extraPath = versionDataDir.getChildFile("Extra").getFullPathName().replaceCharacters("/", "\\");
311349
auto dekenPath = dekenDir.getFullPathName();
312350
auto patchesPath = patchesDir.getFullPathName();
313-
314-
// Create NTFS directory junctions
315-
OSUtils::createJunction(homeDir.getChildFile("Abstractions").getFullPathName().replaceCharacters("/", "\\").toStdString(), abstractionsPath.toStdString());
316-
OSUtils::createJunction(homeDir.getChildFile("Documentation").getFullPathName().replaceCharacters("/", "\\").toStdString(), documentationPath.toStdString());
317-
OSUtils::createJunction(homeDir.getChildFile("Extra").getFullPathName().replaceCharacters("/", "\\").toStdString(), extraPath.toStdString());
351+
352+
createLinkWithRetry(homeDir.getChildFile("Abstractions"), versionDataDir.getChildFile("Abstractions"));
353+
createLinkWithRetry(homeDir.getChildFile("Documentation"), versionDataDir.getChildFile("Documentation"));
354+
createLinkWithRetry(homeDir.getChildFile("Extra"), versionDataDir.getChildFile("Extra"));
318355

319356
auto oldlocation = File::getSpecialLocation(File::SpecialLocationType::userDocumentsDirectory).getChildFile("plugdata");
320357
auto backupLocation = File::getSpecialLocation(File::SpecialLocationType::userDocumentsDirectory).getChildFile("plugdata.old");
@@ -330,9 +367,9 @@ void PluginProcessor::initialiseFilesystem()
330367
auto shortcut = File::getSpecialLocation(File::SpecialLocationType::userDocumentsDirectory).getChildFile("plugdata.LNK");
331368
ProjectInfo::appDataDir.createShortcut("plugdata", shortcut);
332369
#elif JUCE_IOS
333-
versionDataDir.getChildFile("Abstractions").createSymbolicLink(homeDir.getChildFile("Abstractions"), true);
334-
versionDataDir.getChildFile("Documentation").createSymbolicLink(homeDir.getChildFile("Documentation"), true);
335-
versionDataDir.getChildFile("Extra").createSymbolicLink(homeDir.getChildFile("Extra"), true);
370+
createLinkWithRetry(homeDir.getChildFile("Abstractions"), versionDataDir.getChildFile("Abstractions"));
371+
createLinkWithRetry(homeDir.getChildFile("Documentation"), versionDataDir.getChildFile("Documentation"));
372+
createLinkWithRetry(homeDir.getChildFile("Extra"), versionDataDir.getChildFile("Extra"));
336373

337374
auto docsPatchesDir = File::getSpecialLocation(File::SpecialLocationType::userDocumentsDirectory).getChildFile("Patches");
338375
docsPatchesDir.createDirectory();
@@ -343,12 +380,13 @@ void PluginProcessor::initialiseFilesystem()
343380
}
344381
docsPatchesDir.createSymbolicLink(patchesDir, true);
345382
#else
346-
versionDataDir.getChildFile("Abstractions").createSymbolicLink(homeDir.getChildFile("Abstractions"), true);
347-
versionDataDir.getChildFile("Documentation").createSymbolicLink(homeDir.getChildFile("Documentation"), true);
348-
versionDataDir.getChildFile("Extra").createSymbolicLink(homeDir.getChildFile("Extra"), true);
383+
createLinkWithRetry(homeDir.getChildFile("Abstractions"), versionDataDir.getChildFile("Abstractions"));
384+
createLinkWithRetry(homeDir.getChildFile("Documentation"), versionDataDir.getChildFile("Documentation"));
385+
createLinkWithRetry(homeDir.getChildFile("Extra"), versionDataDir.getChildFile("Extra"));
349386
#endif
350387

351388
initMutex.deleteFile();
389+
return extractionCompleted;
352390
}
353391

354392
void PluginProcessor::updateSearchPaths()

Source/PluginProcessor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class PluginProcessor final : public AudioProcessor
125125

126126
void settingsFileReloaded() override;
127127

128-
static void initialiseFilesystem();
128+
static bool initialiseFilesystem();
129129
void updateSearchPaths();
130130

131131
void sendMidiBuffer(int device, MidiBuffer& buffer);

Source/Utility/OSUtils.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace fs = ghc::filesystem;
3939
# include <stdio.h>
4040
# include <filesystem>
4141

42-
void OSUtils::createJunction(std::string from, std::string to)
42+
bool OSUtils::createJunction(std::string from, std::string to)
4343
{
4444

4545
typedef struct {
@@ -63,7 +63,7 @@ void OSUtils::createJunction(std::string from, std::string to)
6363
strcat(szTarget, "\\");
6464

6565
if (!::CreateDirectory(szJunction, nullptr))
66-
return;
66+
return false;
6767

6868
// Obtain SE_RESTORE_NAME privilege (required for opening a directory)
6969
HANDLE hToken = nullptr;
@@ -76,15 +76,15 @@ void OSUtils::createJunction(std::string from, std::string to)
7676
tp.PrivilegeCount = 1;
7777
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
7878
if (!::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
79-
return;
79+
return false;
8080
} catch (DWORD) {
8181
} // Ignore errors
8282
if (hToken)
8383
::CloseHandle(hToken);
8484

8585
HANDLE hDir = ::CreateFile(szJunction, GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
8686
if (hDir == INVALID_HANDLE_VALUE)
87-
return;
87+
return false;
8888

8989
memset(buf, 0, sizeof(buf));
9090
ReparseBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
@@ -98,15 +98,21 @@ void OSUtils::createJunction(std::string from, std::string to)
9898
DWORD dr = ::GetLastError();
9999
::CloseHandle(hDir);
100100
::RemoveDirectory(szJunction);
101-
return;
101+
return false;
102102
}
103103

104104
::CloseHandle(hDir);
105+
return true;
105106
}
106107

107-
void OSUtils::createHardLink(std::string from, std::string to)
108+
bool OSUtils::createHardLink(std::string from, std::string to)
108109
{
109-
fs::create_hard_link(from, to);
110+
try {
111+
fs::create_hard_link(from, to);
112+
} catch (...) {
113+
return false;
114+
}
115+
return true;
110116
}
111117

112118
// Function to run a command as admin on Windows

Source/Utility/OSUtils.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ struct OSUtils {
2424
static unsigned int keycodeToHID(unsigned int scancode);
2525

2626
#if defined(_WIN32) || defined(_WIN64)
27-
static void createJunction(std::string from, std::string to);
28-
static void createHardLink(std::string from, std::string to);
27+
static bool createJunction(std::string from, std::string to);
28+
static bool createHardLink(std::string from, std::string to);
2929
static bool runAsAdmin(std::string file, std::string lpParameters, void* hWnd);
3030
static void useWindowsNativeDecorations(void* windowHandle, bool rounded);
3131
#elif defined(__unix__) && !defined(__APPLE__)

0 commit comments

Comments
 (0)