From 348f30df4e5f8833911da3b89c20dbba6daf71fb Mon Sep 17 00:00:00 2001 From: nakst <> Date: Thu, 16 Sep 2021 11:34:54 +0100 Subject: [PATCH] finish installer --- apps/installer.cpp | 177 ++++++++++++++++++++++++++------ desktop/desktop.cpp | 6 ++ desktop/os.header | 1 + desktop/prefix.h | 5 + kernel/syscall.cpp | 9 ++ shared/esfs2.h | 240 +++++++++++++++++++++++++++++++------------- shared/strings.cpp | 4 + util/build.c | 4 +- util/build_core.c | 14 ++- 9 files changed, 353 insertions(+), 107 deletions(-) diff --git a/apps/installer.cpp b/apps/installer.cpp index 85e8c38..8f0b9c2 100644 --- a/apps/installer.cpp +++ b/apps/installer.cpp @@ -1,8 +1,3 @@ -// TODO Report errors. -// TODO GPT support. -// TODO Handle crashing? -// TODO Write any modified settings during installation. - #define INSTALLER #define ES_CRT_WITHOUT_PREFIX @@ -16,8 +11,7 @@ #include #define Log(...) -// TODO Error handling. -#define exit(x) EsAssert(false) +#define EsFSError() EsAssert(false) #include // Assume an additional 64MB of storage capacity is needed on top of totalUncompressedBytes. @@ -27,6 +21,7 @@ struct InstallerMetadata { uint64_t totalUncompressedBytes; + uint64_t crc64; }; const EsStyle styleRoot = { @@ -76,6 +71,8 @@ EsPanel *panelCustomizeOptions; EsPanel *panelLicenses; EsPanel *panelWait; EsPanel *panelComplete; +EsPanel *panelError; +EsPanel *panelNotSupported; EsTextbox *userNameTextbox; EsTextDisplay *progressDisplay; const char *cSelectedFont; @@ -90,6 +87,8 @@ EsFileOffset partitionBytes; EsUniqueIdentifier installationIdentifier; EsMountPoint newFileSystemMountPoint; EsHandle mountNewFileSystemEvent; +EsError installError; +bool archiveCRCError; ///////////////////////////////////////////// @@ -160,7 +159,7 @@ EsError Extract(const char *pathIn, size_t pathInBytes, const char *pathOut, siz e->inFileOffset = sizeof(header); - uint64_t crc64 = 0, actualCRC64 = 0; + uint64_t crc64 = 0; uint64_t totalBytesExtracted = 0; uint8_t lastProgressByte = 0; @@ -169,7 +168,6 @@ EsError Extract(const char *pathIn, size_t pathInBytes, const char *pathOut, siz while (true) { uint64_t fileSize; if (!Decompress(e, &fileSize, sizeof(fileSize))) break; - actualCRC64 = fileSize; uint16_t nameBytes; if (!Decompress(e, &nameBytes, sizeof(nameBytes))) break; if (nameBytes > NAME_MAX - pathOutBytes) break; @@ -209,46 +207,56 @@ EsError Extract(const char *pathIn, size_t pathInBytes, const char *pathOut, siz LzmaDec_Free(&e->state, &decompressAllocator); EsHandleClose(e->fileIn.handle); - return crc64 == actualCRC64 ? ES_SUCCESS : ES_ERROR_CORRUPT_DATA; + return crc64 == metadata->crc64 ? ES_SUCCESS : ES_ERROR_CORRUPT_DATA; } ///////////////////////////////////////////// -// TODO Error handling. - uint64_t writeOffset; uint64_t writeBytes; uint8_t writeBuffer[BUFFER_SIZE]; +EsError formatError = ES_SUCCESS; -void FlushWriteBuffer() { - if (!writeBytes) return; +bool FlushWriteBuffer() { + if (!writeBytes) return true; EsFileOffset parameters[2] = { partitionOffset * blockDeviceInformation.sectorSize + writeOffset, writeBytes }; EsError error = EsDeviceControl(driveHandle, ES_DEVICE_CONTROL_BLOCK_WRITE, writeBuffer, parameters); - EsAssert(error == ES_SUCCESS); writeBytes = 0; + if (error != ES_SUCCESS) formatError = error; + return error == ES_SUCCESS; } -void ReadBlock(uint64_t block, uint64_t count, void *buffer) { +bool ReadBlock(uint64_t block, uint64_t count, void *buffer) { + if (!FlushWriteBuffer()) { + return false; + } + EsFileOffset parameters[2] = { partitionOffset * blockDeviceInformation.sectorSize + block * blockSize, count * blockSize }; EsError error = EsDeviceControl(driveHandle, ES_DEVICE_CONTROL_BLOCK_READ, buffer, parameters); - EsAssert(error == ES_SUCCESS); + if (error != ES_SUCCESS) formatError = error; + return error == ES_SUCCESS; } -void WriteBlock(uint64_t block, uint64_t count, void *buffer) { +bool WriteBlock(uint64_t block, uint64_t count, void *buffer) { uint64_t offset = block * blockSize, bytes = count * blockSize; if (writeBytes && writeOffset + writeBytes == offset && writeBytes + bytes < sizeof(writeBuffer)) { EsMemoryCopy(writeBuffer + writeBytes, buffer, bytes); writeBytes += bytes; + return true; } else { - FlushWriteBuffer(); + if (!FlushWriteBuffer()) { + return false; + } + writeOffset = offset; writeBytes = bytes; EsMemoryCopy(writeBuffer, buffer, bytes); + return true; } } -void WriteBytes(uint64_t byteOffset, uint64_t byteCount, void *buffer) { +bool WriteBytes(uint64_t byteOffset, uint64_t byteCount, void *buffer) { uint64_t firstSector = byteOffset / blockDeviceInformation.sectorSize; uint64_t lastSector = (byteOffset + byteCount - 1) / blockDeviceInformation.sectorSize; uint64_t sectorCount = lastSector - firstSector + 1; @@ -259,16 +267,25 @@ void WriteBytes(uint64_t byteOffset, uint64_t byteCount, void *buffer) { if (i > 0 && i < sectorCount - 1) continue; EsFileOffset parameters[2] = { (partitionOffset + firstSector + i) * blockDeviceInformation.sectorSize, blockDeviceInformation.sectorSize } ; EsError error = EsDeviceControl(driveHandle, ES_DEVICE_CONTROL_BLOCK_READ, (uint8_t *) buffer2 + i * blockDeviceInformation.sectorSize, parameters); - EsAssert(error == ES_SUCCESS); + + if (error != ES_SUCCESS) { + formatError = error; + return false; + } } EsMemoryCopy((uint8_t *) buffer2 + byteOffset % blockDeviceInformation.sectorSize, buffer, byteCount); EsFileOffset parameters[2] = { (partitionOffset + firstSector) * blockDeviceInformation.sectorSize, sectorCount * blockDeviceInformation.sectorSize }; EsError error = EsDeviceControl(driveHandle, ES_DEVICE_CONTROL_BLOCK_WRITE, buffer, parameters); - EsAssert(error == ES_SUCCESS); + + if (error != ES_SUCCESS) { + formatError = error; + return false; + } EsHeapFree(buffer2); + return true; } ///////////////////////////////////////////// @@ -828,6 +845,10 @@ EsError Install() { Format(partitionBytes, interfaceString_InstallerVolumeLabel, installationIdentifier, kernel, kernelBytes); FlushWriteBuffer(); + if (formatError != ES_SUCCESS) { + return formatError; + } + m.user.context1.u = 8; EsMessagePost(nullptr, &m); @@ -843,22 +864,60 @@ EsError Install() { } error = Extract(EsLiteral("0:/installer_archive.dat"), newFileSystemMountPoint.prefix, newFileSystemMountPoint.prefixBytes); - if (error != ES_SUCCESS) return error; + + if (error == ES_ERROR_CORRUPT_DATA) { + archiveCRCError = true; + } + + if (error != ES_SUCCESS) { + return error; + } return ES_SUCCESS; } void InstallThread(EsGeneric) { EsPerformanceTimerPush(); - EsError error = Install(); - EsAssert(error == ES_SUCCESS); // TODO Reporting errors. - EsPrint("Installation finished in %Fs. Extracted %D from the archive.\n", EsPerformanceTimerPop(), metadata->totalUncompressedBytes); + installError = Install(); + EsPrint("Installation finished in %Fs. Extracted %D from the archive. Error code = %d.\n", EsPerformanceTimerPop(), metadata->totalUncompressedBytes, installError); EsMessage m = { MSG_SET_PROGRESS }; m.user.context1.u = 100; EsMessagePost(nullptr, &m); } +void WriteNewConfiguration() { + size_t newSystemConfigurationPathBytes, newSystemConfigurationBytes; + char *newSystemConfigurationPath = EsStringAllocateAndFormat(&newSystemConfigurationPathBytes, "%s/Essence/Default.ini", + newFileSystemMountPoint.prefixBytes, newFileSystemMountPoint.prefix); + char *newSystemConfiguration = (char *) EsFileReadAll(newSystemConfigurationPath, newSystemConfigurationPathBytes, &newSystemConfigurationBytes); + + size_t lineBufferBytes = 4096; + char *lineBuffer = (char *) EsHeapAllocate(lineBufferBytes, false); + EsINIState s = { .buffer = newSystemConfiguration, .bytes = newSystemConfigurationBytes }; + EsBuffer buffer = { .canGrow = true }; + + while (EsINIParse(&s)) { + if (!s.sectionClassBytes && 0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("ui")) + && 0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("font_sans"))) { + EsBufferFormat(&buffer, "font_sans=%z\n", cSelectedFont); + } else if (!s.sectionClassBytes && 0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("general")) + && 0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("installation_state"))) { + EsBufferFormat(&buffer, "installation_state=0\n"); + } else { + size_t lineBytes = EsINIFormat(&s, lineBuffer, lineBufferBytes); + EsBufferWrite(&buffer, lineBuffer, lineBytes); + EsAssert(lineBytes < lineBufferBytes); + } + } + + EsFileWriteAll(newSystemConfigurationPath, newSystemConfigurationPathBytes, buffer.out, buffer.position); + + EsHeapFree(newSystemConfigurationPath); + EsHeapFree(buffer.out); + EsHeapFree(lineBuffer); +} + ///////////////////////////////////////////// void ButtonViewLicenses(EsInstance *, EsElement *, EsCommand *) { @@ -894,9 +953,35 @@ void ButtonFont(EsInstance *, EsElement *element, EsCommand *) { } } +void Complete() { + if (installError == ES_SUCCESS) { + WriteNewConfiguration(); + EsPanelSwitchTo(switcher, panelComplete, ES_TRANSITION_FADE_IN); + } else { + EsPanel *row = EsPanelCreate(panelError, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL); + EsIconDisplayCreate(row, ES_FLAGS_DEFAULT, 0, ES_ICON_DIALOG_ERROR); + EsSpacerCreate(row, ES_FLAGS_DEFAULT, 0, 15, 0); + + if (installError == ES_ERROR_INSUFFICIENT_RESOURCES) { + EsTextDisplayCreate(row, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, INTERFACE_STRING(InstallerFailedResources)); + } else if (archiveCRCError) { + EsTextDisplayCreate(row, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, INTERFACE_STRING(InstallerFailedArchiveCRCError)); + } else { + EsTextDisplayCreate(row, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, INTERFACE_STRING(InstallerFailedGeneric)); + } + + EsSpacerCreate(panelError, ES_CELL_FILL); + EsPanel *buttonsRow = EsPanelCreate(panelError, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL, &styleButtonsRow); + EsSpacerCreate(buttonsRow, ES_CELL_H_FILL); + EsButtonOnCommand(EsButtonCreate(buttonsRow, ES_FLAGS_DEFAULT, 0, INTERFACE_STRING(DesktopRestartAction)), ButtonRestart); + + EsPanelSwitchTo(switcher, panelError, ES_TRANSITION_FADE_IN); + } +} + void ButtonFinish(EsInstance *, EsElement *, EsCommand *) { if (progress == 100) { - EsPanelSwitchTo(switcher, panelComplete, ES_TRANSITION_FADE_IN); + Complete(); } else { onWaitScreen = true; EsPanelSwitchTo(switcher, panelWait, ES_TRANSITION_FADE_IN); @@ -960,7 +1045,6 @@ void _start() { { panelInstallOptions = EsPanelCreate(switcher, ES_CELL_H_FILL, &styleRoot); - EsPanelSwitchTo(switcher, panelInstallOptions, ES_TRANSITION_NONE); EsTextDisplayCreate(panelInstallOptions, ES_CELL_H_FILL, ES_STYLE_TEXT_HEADING0, INTERFACE_STRING(InstallerTitle)); EsPanel *drivesPanel = EsPanelCreate(panelInstallOptions, ES_CELL_H_FILL, ES_STYLE_PANEL_INSET); @@ -1049,14 +1133,45 @@ void _start() { EsTextDisplayCreate(panelComplete, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, INTERFACE_STRING(InstallerCompleteFromOther)); } - // TODO Failure messages. - EsSpacerCreate(panelComplete, ES_CELL_FILL); EsPanel *buttonsRow = EsPanelCreate(panelComplete, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL, &styleButtonsRow); EsSpacerCreate(buttonsRow, ES_CELL_H_FILL); EsButtonOnCommand(EsButtonCreate(buttonsRow, ES_FLAGS_DEFAULT, 0, INTERFACE_STRING(DesktopRestartAction)), ButtonRestart); } + { + panelError = EsPanelCreate(switcher, ES_CELL_FILL, &styleRoot); + EsTextDisplayCreate(panelError, ES_CELL_H_FILL, ES_STYLE_TEXT_HEADING0, INTERFACE_STRING(InstallerTitle)); + // Contents is created in Complete(). + } + + { + panelNotSupported = EsPanelCreate(switcher, ES_CELL_FILL, &styleRoot); + EsTextDisplayCreate(panelNotSupported, ES_CELL_H_FILL, ES_STYLE_TEXT_HEADING0, INTERFACE_STRING(InstallerTitle)); + + EsPanel *row = EsPanelCreate(panelNotSupported, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL); + EsIconDisplayCreate(row, ES_FLAGS_DEFAULT, 0, ES_ICON_DIALOG_ERROR); + EsSpacerCreate(row, ES_FLAGS_DEFAULT, 0, 15, 0); + + EsTextDisplayCreate(row, ES_CELL_H_FILL, ES_STYLE_TEXT_PARAGRAPH, INTERFACE_STRING(InstallerNotSupported)); + + EsSpacerCreate(panelNotSupported, ES_CELL_FILL); + EsPanel *buttonsRow = EsPanelCreate(panelNotSupported, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL, &styleButtonsRow); + EsSpacerCreate(buttonsRow, ES_CELL_H_FILL); + EsButtonOnCommand(EsButtonCreate(buttonsRow, ES_FLAGS_DEFAULT, 0, INTERFACE_STRING(DesktopRestartAction)), ButtonRestart); + } + + { + MemoryAvailable available; + EsSyscall(ES_SYSCALL_MEMORY_GET_AVAILABLE, (uintptr_t) &available, 0, 0, 0); + + if (available.total < 64 * 1024 * 1024) { + EsPanelSwitchTo(switcher, panelNotSupported, ES_TRANSITION_NONE); + } else { + EsPanelSwitchTo(switcher, panelInstallOptions, ES_TRANSITION_NONE); + } + } + EsDeviceEnumerate([] (EsMessageDevice device, EsGeneric) { ConnectedDriveAdd(device); }, 0); @@ -1101,7 +1216,7 @@ void _start() { if (onWaitScreen && progress == 100) { onWaitScreen = false; - EsPanelSwitchTo(switcher, panelComplete, ES_TRANSITION_FADE_IN); + Complete(); } } } diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 7089c88..0c5d62e 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -1654,6 +1654,12 @@ void ApplicationInstanceCrashed(EsMessage *message) { } application->singleInstance = nullptr; + + if (desktop.installationState == INSTALLATION_STATE_INSTALLER && desktop.installer == application) { + // Restart the installer. + ApplicationInstanceCreate(desktop.installer->id, nullptr, nullptr, true /* hidden */); + } + ApplicationTemporaryDestroy(application); } diff --git a/desktop/os.header b/desktop/os.header index ac97a90..c3c3f71 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -762,6 +762,7 @@ enum EsSyscallType { ES_SYSCALL_MEMORY_OPEN ES_SYSCALL_MEMORY_COMMIT ES_SYSCALL_MEMORY_FAULT_RANGE + ES_SYSCALL_MEMORY_GET_AVAILABLE // Processing. diff --git a/desktop/prefix.h b/desktop/prefix.h index 8f2419c..5aed781 100644 --- a/desktop/prefix.h +++ b/desktop/prefix.h @@ -274,6 +274,11 @@ struct BundleFile { uint64_t offset; }; +struct MemoryAvailable { + size_t available; + size_t total; +}; + #ifdef KERNEL #define K_BOOT_DRIVE "" #else diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index 09a054b..22086fe 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -217,6 +217,15 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_MEMORY_FAULT_RANGE) { SYSCALL_RETURN(success ? ES_SUCCESS : ES_FATAL_ERROR_INVALID_BUFFER, !success); } +SYSCALL_IMPLEMENT(ES_SYSCALL_MEMORY_GET_AVAILABLE) { + MemoryAvailable available; + EsMemoryZero(&available, sizeof(MemoryAvailable)); + available.available = MM_REMAINING_COMMIT() * K_PAGE_SIZE; + available.total = pmm.commitLimit * K_PAGE_SIZE; + SYSCALL_WRITE(argument0, &available, sizeof(MemoryAvailable)); + SYSCALL_RETURN(ES_SUCCESS, false); +} + SYSCALL_IMPLEMENT(ES_SYSCALL_PROCESS_CREATE) { EsProcessCreationArguments arguments; SYSCALL_READ(&arguments, argument0, sizeof(EsProcessCreationArguments)); diff --git a/shared/esfs2.h b/shared/esfs2.h index 88fa3f1..c8d122c 100644 --- a/shared/esfs2.h +++ b/shared/esfs2.h @@ -267,23 +267,32 @@ Superblock superblock; GroupDescriptor *groupDescriptorTable; uint64_t copiedCount; -void ReadBlock(uint64_t block, uint64_t count, void *buffer); -void WriteBlock(uint64_t block, uint64_t count, void *buffer); -void WriteBytes(uint64_t offset, uint64_t count, void *buffer); +bool ReadBlock(uint64_t block, uint64_t count, void *buffer); +bool WriteBlock(uint64_t block, uint64_t count, void *buffer); +bool WriteBytes(uint64_t offset, uint64_t count, void *buffer); -void ReadDirectoryEntryReference(DirectoryEntryReference reference, DirectoryEntry *entry) { +bool ReadDirectoryEntryReference(DirectoryEntryReference reference, DirectoryEntry *entry) { uint8_t buffer[superblock.blockSize]; - ReadBlock(reference.block, 1, buffer); + + if (!ReadBlock(reference.block, 1, buffer)) { + return false; + } + memcpy(entry, buffer + reference.offsetIntoBlock, sizeof(DirectoryEntry)); + return true; } -void WriteDirectoryEntryReference(DirectoryEntryReference reference, DirectoryEntry *entry) { +bool WriteDirectoryEntryReference(DirectoryEntryReference reference, DirectoryEntry *entry) { entry->checksum = 0; entry->checksum = CalculateCRC32(entry, sizeof(DirectoryEntry), 0); uint8_t buffer[superblock.blockSize]; - ReadBlock(reference.block, 1, buffer); - memcpy(buffer + reference.offsetIntoBlock, entry, sizeof(DirectoryEntry)); - WriteBlock(reference.block, 1, buffer); + + if (ReadBlock(reference.block, 1, buffer)) { + memcpy(buffer + reference.offsetIntoBlock, entry, sizeof(DirectoryEntry)); + return WriteBlock(reference.block, 1, buffer); + } else { + return false; + } } Attribute *FindAttribute(DirectoryEntry *entry, uint16_t type) { @@ -295,7 +304,7 @@ Attribute *FindAttribute(DirectoryEntry *entry, uint16_t type) { if (count++ == entry->attributeCount) { Log("Could not find attribute %d.\n", type); - exit(1); + EsFSError(); } } @@ -346,7 +355,7 @@ IndexKey *InsertKeyIntoVertex(uint64_t newKey, IndexVertex *vertex) { return insertionPosition; } -void AllocateExtent(uint64_t increaseBlocks, uint64_t *extentStart, uint64_t *extentCount) { +bool AllocateExtent(uint64_t increaseBlocks, uint64_t *extentStart, uint64_t *extentCount) { // Log("used %ld/%ld, need %ld more\n", superblock.blocksUsed, superblock.blockCount, increaseBlocks); // Find a group to allocate the next extent from. @@ -373,7 +382,7 @@ void AllocateExtent(uint64_t increaseBlocks, uint64_t *extentStart, uint64_t *ex if (!target) { Log("Out of space.\n"); - exit(1); + EsFSError(); } // Load the bitmap, find the largest extent, and mark it as in use. @@ -382,7 +391,9 @@ void AllocateExtent(uint64_t increaseBlocks, uint64_t *extentStart, uint64_t *ex { if (target->blockBitmap) { - ReadBlock(target->blockBitmap, superblock.blocksPerGroupBlockBitmap, bitmap); + if (!ReadBlock(target->blockBitmap, superblock.blocksPerGroupBlockBitmap, bitmap)) { + return false; + } } else { memset(bitmap, 0, superblock.blocksPerGroupBlockBitmap * superblock.blockSize); for (uint64_t i = 0; i < superblock.blocksPerGroupBlockBitmap; i++) bitmap[i / 8] |= 1 << (i % 8); @@ -453,25 +464,28 @@ void AllocateExtent(uint64_t increaseBlocks, uint64_t *extentStart, uint64_t *ex target->checksum = 0; target->checksum = CalculateCRC32(target, sizeof(GroupDescriptor), 0); - WriteBlock(target->blockBitmap, superblock.blocksPerGroupBlockBitmap, bitmap); + if (!WriteBlock(target->blockBitmap, superblock.blocksPerGroupBlockBitmap, bitmap)) { + return false; + } } *extentStart = *extentStart + (target - groupDescriptorTable) * superblock.blocksPerGroup; superblock.blocksUsed += *extentCount; // Log("allocate extent: %ld -> %ld (for %ld)\n", extentStart, extentStart + extentCount, increaseBlocks); + return true; } -void AccessNode(DirectoryEntry *node, void *buffer, uint64_t offsetIntoFile, uint64_t totalCount, DirectoryEntryReference *reference, bool read) { - if (!totalCount) return; +bool AccessNode(DirectoryEntry *node, void *buffer, uint64_t offsetIntoFile, uint64_t totalCount, DirectoryEntryReference *reference, bool read) { + if (!totalCount) return true; AttributeData *dataAttribute = (AttributeData *) FindAttribute(node, ESFS_ATTRIBUTE_DATA); if (dataAttribute->indirection == ESFS_INDIRECTION_DIRECT && read) { memcpy(buffer, dataAttribute->data + offsetIntoFile, totalCount); - return; + return true; } else if (dataAttribute->indirection == ESFS_INDIRECTION_DIRECT && !read) { assert(!reference); memcpy(dataAttribute->data + offsetIntoFile, buffer, totalCount); - return; + return true; } assert(dataAttribute->indirection == ESFS_INDIRECTION_L1); @@ -521,14 +535,19 @@ void AccessNode(DirectoryEntry *node, void *buffer, uint64_t offsetIntoFile, uin uint8_t blockBuffer[superblock.blockSize]; if (read || count != superblock.blockSize) { - ReadBlock(block, 1, blockBuffer); + if (!ReadBlock(block, 1, blockBuffer)) { + return false; + } } if (read) { memcpy(buffer, blockBuffer + offset, count); } else { memcpy(blockBuffer + offset, buffer, count); - WriteBlock(block, 1, blockBuffer); + + if (!WriteBlock(block, 1, blockBuffer)) { + return false; + } } if (reference) { @@ -543,9 +562,11 @@ void AccessNode(DirectoryEntry *node, void *buffer, uint64_t offsetIntoFile, uin buffer = (uint8_t *) buffer + count; goto next; } + + return true; } -void ResizeNode(DirectoryEntry *entry, uint64_t newSize) { +bool ResizeNode(DirectoryEntry *entry, uint64_t newSize) { assert(newSize >= entry->fileSize); AttributeData *dataAttribute = (AttributeData *) FindAttribute(entry, ESFS_ATTRIBUTE_DATA); @@ -553,7 +574,7 @@ void ResizeNode(DirectoryEntry *entry, uint64_t newSize) { if (newSize < (uint64_t) (dataAttribute->size - dataAttribute->dataOffset) && entry->nodeType == ESFS_NODE_TYPE_FILE) { dataAttribute->indirection = ESFS_INDIRECTION_DIRECT; dataAttribute->count = entry->fileSize = newSize; - return; + return true; } // Log("\tresize to %lu\n", newSize); @@ -588,7 +609,9 @@ void ResizeNode(DirectoryEntry *entry, uint64_t newSize) { uint64_t extentStart, extentCount, encodedLength; uint8_t encode[32]; - AllocateExtent(increaseBlocks, &extentStart, &extentCount); + if (!AllocateExtent(increaseBlocks, &extentStart, &extentCount)) { + return false; + } if (extentStart == previousExtentStart + previousExtentCount) { dataAttribute->count--; @@ -604,7 +627,7 @@ void ResizeNode(DirectoryEntry *entry, uint64_t newSize) { if (offsetIntoExtentList + encodedLength > (uint64_t) (dataAttribute->size - dataAttribute->dataOffset)) { Log("Unimplemented - indirection past L1.\n"); - exit(1); + EsFSError(); } memcpy(extents + offsetIntoExtentList, encode, encodedLength); @@ -615,8 +638,10 @@ void ResizeNode(DirectoryEntry *entry, uint64_t newSize) { } } else { Log("Unimplemented - node truncation.\n"); - exit(1); + EsFSError(); } + + return true; } #if 0 @@ -702,7 +727,7 @@ void NewDirectoryEntry(DirectoryEntry *entry, uint8_t nodeType, EsUniqueIdentifi entry->checksum = CalculateCRC32(entry, sizeof(DirectoryEntry), 0); } -void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, DirectoryEntryReference *outputReference, +bool AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, DirectoryEntryReference *outputReference, DirectoryEntryReference directoryReference) { // Log("add %s to %s\n", name, path); @@ -710,7 +735,7 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di DirectoryEntry directory; - ReadDirectoryEntryReference(directoryReference, &directory); + if (!ReadDirectoryEntryReference(directoryReference, &directory)) return false; AttributeData *dataAttribute = (AttributeData *) FindAttribute(&directory, ESFS_ATTRIBUTE_DATA); AttributeDirectory *directoryAttribute = (AttributeDirectory *) FindAttribute(&directory, ESFS_ATTRIBUTE_DIRECTORY); @@ -720,12 +745,15 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di if (!(directoryAttribute->childNodes % superblock.directoryEntriesPerBlock)) { // Log("increasing directory to fit %ld entries\n========={\n", (directory.fileSize + superblock.blockSize) / sizeof(DirectoryEntry)); - ResizeNode(&directory, directory.fileSize + superblock.blockSize); + if (!ResizeNode(&directory, directory.fileSize + superblock.blockSize)) return false; // Log("========}\n"); } directoryAttribute->childNodes++; - WriteDirectoryEntryReference(directoryReference, &directory); + + if (!WriteDirectoryEntryReference(directoryReference, &directory)) { + return false; + } } // Step 2: Create the directory entry, and write it to the directory. @@ -736,7 +764,10 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di { NewDirectoryEntry(&entry, nodeType, directory.identifier, name); // Log("\tchild nodes: %ld\n", directoryAttribute->childNodes); - AccessNode(&directory, &entry, (directoryAttribute->childNodes - 1) * sizeof(DirectoryEntry), sizeof(DirectoryEntry), &reference, false); + + if (!AccessNode(&directory, &entry, (directoryAttribute->childNodes - 1) * sizeof(DirectoryEntry), sizeof(DirectoryEntry), &reference, false)) { + return false; + } } // Step 3: Add the node into the index. @@ -756,14 +787,20 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di // Directory is empty - create the root vertex. uint64_t _unused; - AllocateExtent(1, &directoryAttribute->indexRootBlock, &_unused); + + if (!AllocateExtent(1, &directoryAttribute->indexRootBlock, &_unused)) { + return false; + } + blocks[0] = directoryAttribute->indexRootBlock; vertex->maxCount = (superblock.blockSize - ESFS_INDEX_KEY_OFFSET) / sizeof(IndexKey) - 1 /* +1 key */; vertex->offset = ESFS_INDEX_KEY_OFFSET; memcpy(vertex->signature, ESFS_INDEX_VERTEX_SIGNATURE, 4); // Log("rootBlock = %ld for %s\n", directoryAttribute->indexRootBlock, path); } else { - ReadBlock(blocks[0], 1, vertex); + if (!ReadBlock(blocks[0], 1, vertex)) { + return false; + } // Log("start = %ld for %s\n", blocks[0], path); } @@ -772,7 +809,7 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di if (ESFS_VERTEX_KEY(vertex, i)->value == newKey) { // The key is already in the tree. Log("The file already exists."); - exit(1); + EsFSError(); } } @@ -781,7 +818,11 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di if ((i == vertex->count || newKey < key->value) && key->child) { blocks[++depth] = key->child; - ReadBlock(key->child, 1, vertex); + + if (!ReadBlock(key->child, 1, vertex)) { + return false; + } + goto next; } } @@ -809,7 +850,7 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di // Create a new sibling. uint64_t siblingBlock = 0, _unused; - AllocateExtent(1, &siblingBlock, &_unused); + if (!AllocateExtent(1, &siblingBlock, &_unused)) return false; IndexVertex *sibling = (IndexVertex *) _buffer0; sibling->maxCount = (superblock.blockSize - ESFS_INDEX_KEY_OFFSET) / sizeof(IndexKey) - 1 /* +1 key */; sibling->offset = ESFS_INDEX_KEY_OFFSET; @@ -829,7 +870,7 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di depth++; uint64_t _unused; - AllocateExtent(1, &blocks[0], &_unused); + if (!AllocateExtent(1, &blocks[0], &_unused)) return false; parent->maxCount = (superblock.blockSize - ESFS_INDEX_KEY_OFFSET) / sizeof(IndexKey) - 1 /* +1 key */; parent->offset = ESFS_INDEX_KEY_OFFSET; @@ -841,7 +882,9 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di parent->keys[0].child = blocks[1]; directoryAttribute->indexRootBlock = blocks[0]; } else { - ReadBlock(blocks[depth - 1], 1, parent); + if (!ReadBlock(blocks[depth - 1], 1, parent)) { + return false; + } } IndexKey *parentKeys = (IndexKey *) ((uint8_t *) parent + parent->offset); @@ -886,8 +929,8 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di sibling->checksum = 0; sibling->checksum = CalculateCRC32(sibling, superblock.blockSize, 0); vertex->checksum = 0; vertex->checksum = CalculateCRC32(vertex, superblock.blockSize, 0); - WriteBlock(siblingBlock, 1, sibling); - WriteBlock(blocks[depth], 1, vertex); + if (!WriteBlock(siblingBlock, 1, sibling)) return false; + if (!WriteBlock(blocks[depth], 1, vertex)) return false; // Check if the parent vertex is full. @@ -898,33 +941,49 @@ void AddNode(const char *name, uint8_t nodeType, DirectoryEntry *outputEntry, Di // Write the block. vertex->checksum = 0; vertex->checksum = CalculateCRC32(vertex, superblock.blockSize, 0); - WriteBlock(blocks[depth], 1, vertex); + if (!WriteBlock(blocks[depth], 1, vertex)) return false; } if (outputEntry) *outputEntry = entry; if (outputReference) *outputReference = reference; // PrintTree(directoryAttribute->indexRootBlock); - WriteDirectoryEntryReference(directoryReference, &directory); + if (!WriteDirectoryEntryReference(directoryReference, &directory)) { + return false; + } + + return true; } -void MountVolume() { +bool MountVolume() { // Read the superblock. blockSize = ESFS_BOOT_SUPER_BLOCK_SIZE; - ReadBlock(1, 1, &superblock); + + if (!ReadBlock(1, 1, &superblock)) { + return false; + } if (superblock.mounted) { Log("EsFS: Volume not unmounted, exiting...\n"); - exit(1); + EsFSError(); } superblock.mounted = 1; - WriteBlock(1, 1, &superblock); + + if (!WriteBlock(1, 1, &superblock)) { + return false; + } + blockSize = superblock.blockSize; // Read the group descriptor table. groupDescriptorTable = (GroupDescriptor *) malloc(superblock.groupCount * sizeof(GroupDescriptor) + superblock.blockSize - 1); - ReadBlock(superblock.gdtFirstBlock, (superblock.groupCount * sizeof(GroupDescriptor) + superblock.blockSize - 1) / superblock.blockSize, groupDescriptorTable); + + if (!ReadBlock(superblock.gdtFirstBlock, (superblock.groupCount * sizeof(GroupDescriptor) + superblock.blockSize - 1) / superblock.blockSize, groupDescriptorTable)) { + return false; + } + + return true; } void UnmountVolume() { @@ -939,11 +998,14 @@ void UnmountVolume() { bool FindNode(const char *cName, DirectoryEntry *node, DirectoryEntryReference directoryReference) { DirectoryEntry directory; - ReadDirectoryEntryReference(directoryReference, &directory); + if (!ReadDirectoryEntryReference(directoryReference, &directory)) return false; AttributeDirectory *directoryAttribute = (AttributeDirectory *) FindAttribute(&directory, ESFS_ATTRIBUTE_DIRECTORY); for (uintptr_t i = 0; i < directoryAttribute->childNodes; i++) { - AccessNode(&directory, node, sizeof(DirectoryEntry) * i, sizeof(DirectoryEntry), NULL, true); + if (!AccessNode(&directory, node, sizeof(DirectoryEntry) * i, sizeof(DirectoryEntry), NULL, true)) { + return false; + } + AttributeFilename *filename = (AttributeFilename *) FindAttribute(node, ESFS_ATTRIBUTE_FILENAME); if (filename->length == strlen(cName) && 0 == memcmp(filename->filename, cName, filename->length)) { @@ -973,7 +1035,7 @@ typedef struct ImportNode { bool isFile; } ImportNode; -uint64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) { +int64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) { uint64_t totalSize = 0; for (uintptr_t i = 0; i < arrlenu(node.children); i++) { @@ -989,24 +1051,36 @@ uint64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) { DirectoryEntryReference reference; DirectoryEntry entry; - AddNode(node.children[i].name, ESFS_NODE_TYPE_FILE, &entry, &reference, parentDirectory); - ResizeNode(&entry, fileLength); + if (!AddNode(node.children[i].name, ESFS_NODE_TYPE_FILE, &entry, &reference, parentDirectory)) { + return -1; + } + + if (!ResizeNode(&entry, fileLength)) { + return -1; + } + totalSize += fileLength; - AccessNode(&entry, data, 0, fileLength, NULL, false); - WriteDirectoryEntryReference(reference, &entry); + if (!AccessNode(&entry, data, 0, fileLength, NULL, false)) { + return -1; + } + + if (!WriteDirectoryEntryReference(reference, &entry)) { + return -1; + } free(data); } } else { DirectoryEntryReference reference; - AddNode(node.children[i].name, ESFS_NODE_TYPE_DIRECTORY, NULL, &reference, parentDirectory); - uint64_t size = Import(node.children[i], reference); + if (!AddNode(node.children[i].name, ESFS_NODE_TYPE_DIRECTORY, NULL, &reference, parentDirectory)) return -1; + int64_t size = Import(node.children[i], reference); + if (size == -1) return -1; DirectoryEntry directory; - ReadDirectoryEntryReference(reference, &directory); + if (!ReadDirectoryEntryReference(reference, &directory)) return -1; AttributeDirectory *directoryAttribute = (AttributeDirectory *) FindAttribute(&directory, ESFS_ATTRIBUTE_DIRECTORY); directoryAttribute->totalSize = size; - WriteDirectoryEntryReference(reference, &directory); + if (!WriteDirectoryEntryReference(reference, &directory)) return -1; totalSize += size; } } @@ -1015,18 +1089,18 @@ uint64_t Import(ImportNode node, DirectoryEntryReference parentDirectory) { } #endif -void Format(uint64_t driveSize, const char *volumeName, EsUniqueIdentifier osInstallation, +bool Format(uint64_t driveSize, const char *volumeName, EsUniqueIdentifier osInstallation, void *kernel, size_t kernelBytes) { assert(sizeof(Superblock) == 8192); if (driveSize < ESFS_DRIVE_MINIMUM_SIZE) { Log("Error: Cannot create a drive of %d bytes (too small).\n", (int) driveSize); - exit(1); + EsFSError(); } if (strlen(volumeName) > ESFS_MAXIMUM_VOLUME_NAME_LENGTH) { Log("Error: Volume name '%s' is too long; must be <= %d bytes.\n", volumeName, (int) ESFS_MAXIMUM_VOLUME_NAME_LENGTH); - exit(1); + EsFSError(); } // Format the volume. @@ -1092,9 +1166,15 @@ void Format(uint64_t driveSize, const char *volumeName, EsUniqueIdentifier osIns entry->checksum = CalculateCRC32(entry, sizeof(DirectoryEntry), 0); } - WriteBytes(blockCoreNodes * superblock.blockSize, sizeof(coreNodes), &coreNodes); + if (!WriteBytes(blockCoreNodes * superblock.blockSize, sizeof(coreNodes), &coreNodes)) { + return false; + } + superblock.checksum = CalculateCRC32(&superblock, sizeof(Superblock), 0); - WriteBytes(ESFS_BOOT_SUPER_BLOCK_SIZE, ESFS_BOOT_SUPER_BLOCK_SIZE, &superblock); + + if (!WriteBytes(ESFS_BOOT_SUPER_BLOCK_SIZE, ESFS_BOOT_SUPER_BLOCK_SIZE, &superblock)) { + return false; + } { GroupDescriptor *buffer = (GroupDescriptor *) malloc(superblock.groupCount * sizeof(GroupDescriptor)); @@ -1112,34 +1192,52 @@ void Format(uint64_t driveSize, const char *volumeName, EsUniqueIdentifier osIns buffer[i].blockBitmap = blockGroup0Bitmap; buffer[i].bitmapChecksum = CalculateCRC32(firstGroupBitmap, sizeof(firstGroupBitmap), 0); buffer[i].largestExtent = superblock.blocksPerGroup - superblock.blocksUsed; - WriteBytes(blockGroup0Bitmap * superblock.blockSize, sizeof(firstGroupBitmap), &firstGroupBitmap); + + if (!WriteBytes(blockGroup0Bitmap * superblock.blockSize, sizeof(firstGroupBitmap), &firstGroupBitmap)) { + return false; + } } buffer[i].checksum = CalculateCRC32(buffer + i, sizeof(GroupDescriptor), 0); } - WriteBytes(superblock.gdtFirstBlock * superblock.blockSize, superblock.groupCount * sizeof(GroupDescriptor), buffer); + if (!WriteBytes(superblock.gdtFirstBlock * superblock.blockSize, superblock.groupCount * sizeof(GroupDescriptor), buffer)) { + return false; + } + free(buffer); } } // Add the kernel. - { - MountVolume(); - + if (MountVolume()) { DirectoryEntryReference reference = superblock.kernel; DirectoryEntry entry; EsUniqueIdentifier unused = {}; NewDirectoryEntry(&entry, ESFS_NODE_TYPE_FILE, unused, "Kernel"); - WriteDirectoryEntryReference(reference, &entry); - ResizeNode(&entry, kernelBytes); - AccessNode(&entry, kernel, 0, kernelBytes, NULL, false); - WriteDirectoryEntryReference(reference, &entry); + + if (WriteDirectoryEntryReference(reference, &entry)) { + if (ResizeNode(&entry, kernelBytes)) { + if (AccessNode(&entry, kernel, 0, kernelBytes, NULL, false)) { + WriteDirectoryEntryReference(reference, &entry); + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } UnmountVolume(); + } else { + return false; } + + return true; } #endif diff --git a/shared/strings.cpp b/shared/strings.cpp index e0961f5..b018c92 100644 --- a/shared/strings.cpp +++ b/shared/strings.cpp @@ -321,6 +321,10 @@ DEFINE_INTERFACE_STRING(InstallerCompleteFromOther, "Installation has completed DEFINE_INTERFACE_STRING(InstallerCompleteFromUSB, "Installation has completed successfully. Disconnect the installation USB, and restart your computer."); DEFINE_INTERFACE_STRING(InstallerVolumeLabel, "Essence HD"); DEFINE_INTERFACE_STRING(InstallerUseMBR, "Use legacy BIOS boot (select for older computers)"); +DEFINE_INTERFACE_STRING(InstallerFailedArchiveCRCError, "The installation data has been corrupted. Create a new installation USB or disk, and try again."); +DEFINE_INTERFACE_STRING(InstallerFailedGeneric, "The installation could not complete. This likely means that the drive you selected is failing. Try installing on a different drive."); +DEFINE_INTERFACE_STRING(InstallerFailedResources, "The installation could not complete. Your computer does not have enough memory to install " SYSTEM_BRAND_SHORT); +DEFINE_INTERFACE_STRING(InstallerNotSupported, "Your computer does not meet the minimum system requirements to install " SYSTEM_BRAND_SHORT ". Remove the installer, and restart your computer."); // TODO System Monitor. diff --git a/util/build.c b/util/build.c index 08fd308..71d9717 100644 --- a/util/build.c +++ b/util/build.c @@ -1108,7 +1108,6 @@ void DoCommand(const char *l) { FILE *f = fopen("bin/temp.dat", "wb"); uint64_t crc64 = 0, uncompressed = 0; GatherFilesForInstallerArchive(f, "root", "", &crc64, &uncompressed); - fwrite(&crc64, 1, sizeof(crc64), f); uint32_t sizeMB = ftell(f) / 1000000; fclose(f); printf("Compressing %d MB...\n", sizeMB); @@ -1117,8 +1116,9 @@ void DoCommand(const char *l) { lstat("bin/installer_archive.dat", &s); printf("Compressed to %d MB.\n", (uint32_t) (s.st_size / 1000000)); unlink("bin/temp.dat"); - f = fopen("bin/installer_metadata.dat", "ab"); + f = fopen("bin/installer_metadata.dat", "wb"); fwrite(&uncompressed, 1, sizeof(uncompressed), f); + fwrite(&crc64, 1, sizeof(crc64), f); fclose(f); } else if (0 == strcmp(l, "config")) { BuildUtilities(); diff --git a/util/build_core.c b/util/build_core.c index 30dc69b..9e300bc 100644 --- a/util/build_core.c +++ b/util/build_core.c @@ -129,6 +129,8 @@ File FileOpen(const char *path, char mode) { #endif +#define EsFSError() exit(1) + #include "../shared/hash.cpp" #include "../shared/partitions.cpp" #include "build_common.h" @@ -1058,7 +1060,7 @@ void BuildBootloader(Application *application) { File _drive; uint64_t _partitionOffset; -void ReadBlock(uint64_t block, uint64_t count, void *buffer) { +bool ReadBlock(uint64_t block, uint64_t count, void *buffer) { FileSeek(_drive, block * blockSize + _partitionOffset); // printf("read of block %ld\n", block); @@ -1066,9 +1068,11 @@ void ReadBlock(uint64_t block, uint64_t count, void *buffer) { Log("Error: Could not read blocks %d->%d of drive.\n", (int) block, (int) (block + count)); exit(1); } + + return true; } -void WriteBlock(uint64_t block, uint64_t count, void *buffer) { +bool WriteBlock(uint64_t block, uint64_t count, void *buffer) { FileSeek(_drive, block * blockSize + _partitionOffset); assert(block < 4294967296); @@ -1076,15 +1080,19 @@ void WriteBlock(uint64_t block, uint64_t count, void *buffer) { Log("Error: Could not write to blocks %d->%d of drive.\n", (int) block, (int) (block + count)); exit(1); } + + return true; } -void WriteBytes(uint64_t offset, uint64_t count, void *buffer) { +bool WriteBytes(uint64_t offset, uint64_t count, void *buffer) { FileSeek(_drive, offset + _partitionOffset); if (FileWrite(_drive, count, buffer) != count) { Log("Error: Could not write to bytes %d->%d of drive.\n", (int) offset, (int) (offset + count)); exit(1); } + + return true; } void Install(const char *driveFile, uint64_t partitionSize, const char *partitionLabel) {