files: storing file content type

This commit is contained in:
nakst 2022-03-21 14:25:58 +00:00
parent 82ccd24c61
commit a0fe4d31e9
15 changed files with 158 additions and 53 deletions

View File

@ -337,7 +337,7 @@ void CommandPasteTask(EsUserTask *userTask, EsGeneric _task) {
EsDirectoryChild information; EsDirectoryChild information;
if (EsPathQueryInformation(STRING(source), &information)) { if (ES_SUCCESS == EsPathQueryInformation(STRING(source), &information)) {
if (information.fileSize == -1) { if (information.fileSize == -1) {
// TODO Support progress on volumes that don't report total directory sizes. // TODO Support progress on volumes that don't report total directory sizes.
} else { } else {

View File

@ -17,7 +17,7 @@ Array<Folder *> foldersWithNoAttachedInstances;
bool FSDirHandlesPath(String path) { bool FSDirHandlesPath(String path) {
EsDirectoryChild information; EsDirectoryChild information;
if (EsPathQueryInformation(STRING(path), &information)) { if (ES_SUCCESS == EsPathQueryInformation(STRING(path), &information)) {
return information.type == ES_NODE_DIRECTORY; return information.type == ES_NODE_DIRECTORY;
} else { } else {
return false; return false;
@ -91,7 +91,7 @@ EsError FSDirEnumerate(Folder *folder) {
void FSDirGetTotalSize(Folder *folder) { void FSDirGetTotalSize(Folder *folder) {
EsDirectoryChild information; EsDirectoryChild information;
if (EsPathQueryInformation(STRING(folder->path), &information)) { if (ES_SUCCESS == EsPathQueryInformation(STRING(folder->path), &information)) {
folder->spaceUsed = information.fileSize; folder->spaceUsed = information.fileSize;
folder->spaceTotal = 0; folder->spaceTotal = 0;
} }
@ -464,7 +464,7 @@ void FolderFileUpdatedAtPath(String path, Instance *instance) {
String file = PathGetName(path); String file = PathGetName(path);
String folder = PathGetParent(path); String folder = PathGetParent(path);
EsDirectoryChild information = {}; EsDirectoryChild information = {};
bool add = EsPathQueryInformation(STRING(path), &information); bool add = ES_SUCCESS == EsPathQueryInformation(STRING(path), &information);
for (uintptr_t i = 0; i < loadedFolders.Length(); i++) { for (uintptr_t i = 0; i < loadedFolders.Length(); i++) {
if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue; if (loadedFolders[i]->itemHandler->type != NAMESPACE_HANDLER_FILE_SYSTEM) continue;

View File

@ -378,8 +378,8 @@ int ExternalFileAppend(ExecutionContext *context, Value *returnValue) {
int ExternalFileGetSize(ExecutionContext *context, Value *returnValue) { int ExternalFileGetSize(ExecutionContext *context, Value *returnValue) {
STACK_POP_STRING(entryText, entryBytes); STACK_POP_STRING(entryText, entryBytes);
EsDirectoryChild information; EsDirectoryChild information;
bool exists = EsPathQueryInformation(entryText, entryBytes, &information); EsError error = EsPathQueryInformation(entryText, entryBytes, &information);
if (!exists) RETURN_ERROR(ES_ERROR_FILE_DOES_NOT_EXIST); if (error != ES_SUCCESS) RETURN_ERROR(ES_ERROR_FILE_DOES_NOT_EXIST);
if (information.type != ES_NODE_FILE) RETURN_ERROR(ES_ERROR_INCORRECT_NODE_TYPE); if (information.type != ES_NODE_FILE) RETURN_ERROR(ES_ERROR_INCORRECT_NODE_TYPE);
returnValue->i = information.fileSize; returnValue->i = information.fileSize;
return EXTCALL_RETURN_ERR_UNMANAGED; return EXTCALL_RETURN_ERR_UNMANAGED;
@ -1011,7 +1011,7 @@ void EnterCommand(Instance *instance) {
uint8_t newline = '\n'; uint8_t newline = '\n';
EsFileWriteSync(globalCommandHistoryFile.handle, EsFileGetSize(globalCommandHistoryFile.handle), dataBytes, data); EsFileWriteSync(globalCommandHistoryFile.handle, EsFileGetSize(globalCommandHistoryFile.handle), dataBytes, data);
EsFileWriteSync(globalCommandHistoryFile.handle, EsFileGetSize(globalCommandHistoryFile.handle), 1, &newline); EsFileWriteSync(globalCommandHistoryFile.handle, EsFileGetSize(globalCommandHistoryFile.handle), 1, &newline);
EsFileControl(globalCommandHistoryFile.handle, ES_FILE_CONTROL_FLUSH); EsFileControl(globalCommandHistoryFile.handle, ES_FILE_CONTROL_FLUSH, nullptr, 0);
EsPanel *commandLogRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, EsStyleIntern(&styleInputRow)); EsPanel *commandLogRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, EsStyleIntern(&styleInputRow));
EsTextDisplayCreate(commandLogRow, ES_FLAGS_DEFAULT, EsStyleIntern(&stylePromptText), "\u2661"); EsTextDisplayCreate(commandLogRow, ES_FLAGS_DEFAULT, EsStyleIntern(&stylePromptText), "\u2661");

View File

@ -132,7 +132,7 @@ LoadEachExtent:
%endmacro %endmacro
%macro FilesystemSpecificCode 0 %macro FilesystemSpecificCode 0
KernelDataStreamPosition: dw 0x850 KernelDataStreamPosition: dw 0x860
ErrorBadFilesystem: db "Invalid boot EsFS volume.",0 ErrorBadFilesystem: db "Invalid boot EsFS volume.",0
ErrorUnexpectedFileProblem: db "The kernel file could not be loaded.",0 ErrorUnexpectedFileProblem: db "The kernel file could not be loaded.",0
%endmacro %endmacro

View File

@ -1227,7 +1227,7 @@ void EsInstanceSaveComplete(EsInstance *instance, EsFileStore *file, bool succes
} }
if (success) { if (success) {
file->error = EsFileControl(file->handle, ES_FILE_CONTROL_FLUSH); file->error = EsFileControl(file->handle, ES_FILE_CONTROL_FLUSH, nullptr, 0);
if (file->error != ES_SUCCESS) { if (file->error != ES_SUCCESS) {
success = false; success = false;

View File

@ -1305,6 +1305,44 @@ bool ResizeFileTest() {
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
bool FileContentTypeTest() {
int checkIndex = 0;
EsUniqueIdentifier identifier1, identifier2;
for (uintptr_t i = 0; i < 16; i++) identifier1.d[i] = i;
for (uintptr_t i = 0; i < 16; i++) identifier2.d[i] = 0;
size_t fileSize;
uint32_t *fileData = (uint32_t *) EsFileReadAll(EsLiteral("|Settings:/continue.txt"), &fileSize);
if (fileData) {
size_t count;
EsError error;
EsDirectoryChild *array = EsDirectoryEnumerate(EsLiteral("|Settings:/test"), &count, &error);
CHECK(error == ES_SUCCESS);
CHECK(count == 1);
CHECK(0 == EsMemoryCompare(&identifier1, &array[0].contentType, sizeof(EsUniqueIdentifier)));
EsFileInformation information = EsFileOpen(EsLiteral("|Settings:/test/a"), ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
CHECK(information.error == ES_SUCCESS);
CHECK(0 == EsMemoryCompare(&identifier1, &information.contentType, sizeof(EsUniqueIdentifier)));
} else {
uint32_t c = 1;
CHECK(ES_SUCCESS == EsFileWriteAll(EsLiteral("|Settings:/continue.txt"), &c, sizeof(uint32_t)));
EsFileInformation information = EsFileOpen(EsLiteral("|Settings:/test/a"), ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
CHECK(information.error == ES_SUCCESS);
CHECK(0 == EsMemoryCompare(&identifier2, &information.contentType, sizeof(EsUniqueIdentifier)));
CHECK(ES_SUCCESS == EsFileControl(information.handle, ES_FILE_CONTROL_SET_CONTENT_TYPE, &identifier1, sizeof(identifier1)));
EsSyscall(ES_SYSCALL_SHUTDOWN, ES_SHUTDOWN_ACTION_RESTART, 0, 0, 0);
while (EsMessageReceive());
}
return true;
}
//////////////////////////////////////////////////////////////
#endif #endif
const Test tests[] = { const Test tests[] = {
@ -1323,6 +1361,7 @@ const Test tests[] = {
TEST(POSIXSubsystemTest, 120), TEST(POSIXSubsystemTest, 120),
TEST(RestartTest, 1200), TEST(RestartTest, 1200),
TEST(ResizeFileTest, 600), TEST(ResizeFileTest, 600),
TEST(FileContentTypeTest, 60),
}; };
#ifndef API_TESTS_FOR_RUNNER #ifndef API_TESTS_FOR_RUNNER

View File

@ -71,16 +71,17 @@ bool EsPathExists(const char *path, ptrdiff_t pathBytes, EsNodeType *type) {
return true; return true;
} }
bool EsPathQueryInformation(const char *path, ptrdiff_t pathBytes, EsDirectoryChild *information) { EsError EsPathQueryInformation(const char *path, ptrdiff_t pathBytes, EsDirectoryChild *information) {
if (pathBytes == -1) pathBytes = EsCStringLength(path); if (pathBytes == -1) pathBytes = EsCStringLength(path);
_EsNodeInformation node = {}; _EsNodeInformation node = {};
EsError error = NodeOpen(path, pathBytes, ES_NODE_FAIL_IF_NOT_FOUND, &node); EsError error = NodeOpen(path, pathBytes, ES_NODE_FAIL_IF_NOT_FOUND, &node);
if (error != ES_SUCCESS) return false; if (error != ES_SUCCESS) return error;
EsHandleClose(node.handle); EsHandleClose(node.handle);
information->type = node.type; information->type = node.type;
information->fileSize = node.fileSize; information->fileSize = node.fileSize;
information->directoryChildren = node.directoryChildren; information->directoryChildren = node.directoryChildren;
return true; information->contentType = node.contentType;
return ES_SUCCESS;
} }
EsError EsPathCreate(const char *path, ptrdiff_t pathBytes, EsNodeType type, bool createLeadingDirectories) { EsError EsPathCreate(const char *path, ptrdiff_t pathBytes, EsNodeType type, bool createLeadingDirectories) {
@ -94,8 +95,8 @@ EsError EsPathCreate(const char *path, ptrdiff_t pathBytes, EsNodeType type, boo
return ES_SUCCESS; return ES_SUCCESS;
} }
EsError EsFileControl(EsHandle file, uint32_t flags) { EsError EsFileControl(EsHandle file, EsFileControlOperation operation, const void *data, size_t dataBytes) {
return EsSyscall(ES_SYSCALL_FILE_CONTROL, file, flags, 0, 0); return EsSyscall(ES_SYSCALL_FILE_CONTROL, file, operation, (uintptr_t) data, dataBytes);
} }
ptrdiff_t DirectoryEnumerateFromHandle(EsHandle directory, EsDirectoryChild *buffer, size_t size) { ptrdiff_t DirectoryEnumerateFromHandle(EsHandle directory, EsDirectoryChild *buffer, size_t size) {
@ -167,6 +168,7 @@ EsFileInformation EsFileOpen(const char *path, ptrdiff_t pathLength, uint32_t fl
if (result == ES_SUCCESS) { if (result == ES_SUCCESS) {
information.handle = node.handle; information.handle = node.handle;
information.size = node.fileSize; information.size = node.fileSize;
information.contentType = node.contentType;
} }
information.error = result; information.error = result;
@ -245,7 +247,7 @@ EsFileOffsetDifference EsFileStoreGetSize(EsFileStore *file) {
} else if (file->type == FILE_STORE_PATH) { } else if (file->type == FILE_STORE_PATH) {
EsDirectoryChild information; EsDirectoryChild information;
if (EsPathQueryInformation(file->path, file->pathBytes, &information)) { if (ES_SUCCESS == EsPathQueryInformation(file->path, file->pathBytes, &information)) {
return file->pathBytes; return file->pathBytes;
} else { } else {
return -1; return -1;
@ -576,7 +578,7 @@ EsError EsFileWriteAllGatherFromHandle(EsHandle handle, const void **data, const
offset += sizes[i]; offset += sizes[i];
} }
error = EsFileControl(handle, ES_FILE_CONTROL_FLUSH); error = EsFileControl(handle, ES_FILE_CONTROL_FLUSH, nullptr, 0);
return error; return error;
} }

View File

@ -760,8 +760,9 @@ inttype EsConnectionOpenFlags uint32_t none {
ES_CONNECTION_OPEN_WAIT = bit 0 ES_CONNECTION_OPEN_WAIT = bit 0
}; };
inttype EsFileControlFlags uint32_t none { inttype EsFileControlOperation uint32_t none {
ES_FILE_CONTROL_FLUSH = bit 0 ES_FILE_CONTROL_FLUSH = 0 // data/dataBytes ignored
ES_FILE_CONTROL_SET_CONTENT_TYPE = 1 // data EsUniqueIdentifier; dataBytes ignored
}; };
inttype EsElementUpdateContentFlags uint32_t none { inttype EsElementUpdateContentFlags uint32_t none {
@ -838,6 +839,7 @@ inttype EsProcessCreationFlags uint32_t none {
inttype EsVolumeFlags uint32_t none { inttype EsVolumeFlags uint32_t none {
ES_VOLUME_READ_ONLY = bit 0 ES_VOLUME_READ_ONLY = bit 0
ES_VOLUME_STORES_CONTENT_TYPE = bit 1
}; };
inttype EsPathMoveFlags uint32_t none { inttype EsPathMoveFlags uint32_t none {
@ -1340,6 +1342,7 @@ struct EsFileInformation {
EsHandle handle; EsHandle handle;
EsFileOffset size; EsFileOffset size;
EsError error; EsError error;
EsUniqueIdentifier contentType; // Zeroed if unsupported, or not initialised.
}; };
struct EsDirectoryChild { struct EsDirectoryChild {
@ -1348,6 +1351,7 @@ struct EsDirectoryChild {
EsNodeType type; EsNodeType type;
EsFileOffsetDifference fileSize; // -1 if unsupported. EsFileOffsetDifference fileSize; // -1 if unsupported.
EsFileOffsetDifference directoryChildren; // ES_DIRECTORY_CHILDREN_UNKNOWN if unsupported. EsFileOffsetDifference directoryChildren; // ES_DIRECTORY_CHILDREN_UNKNOWN if unsupported.
EsUniqueIdentifier contentType; // Zeroed if unsupported, or not initialised.
}; };
struct EsPoint { struct EsPoint {
@ -1635,6 +1639,7 @@ private struct _EsNodeInformation {
EsFileOffset fileSize; EsFileOffset fileSize;
EsFileOffsetDifference directoryChildren; EsFileOffsetDifference directoryChildren;
EsNodeType type; EsNodeType type;
EsUniqueIdentifier contentType;
}; };
struct EsVolumeInformation { struct EsVolumeInformation {
@ -2137,7 +2142,7 @@ function EsError EsFileWriteAllGatherFromHandle(EsHandle handle, const void **da
function void *EsFileMap(STRING filePath, size_t *fileSize, uint32_t flags) @native(); function void *EsFileMap(STRING filePath, size_t *fileSize, uint32_t flags) @native();
function EsError EsFileCopy(STRING source, STRING destination, void **copyBuffer = ES_NULL, EsFileCopyCallback callback = ES_NULL, EsGeneric data = ES_NULL) @todo(); // If you are copying lots of files, you can reuse the temporary copy buffer by storing the output copyBuffer; call EsHeapFree on it after the last copy. function EsError EsFileCopy(STRING source, STRING destination, void **copyBuffer = ES_NULL, EsFileCopyCallback callback = ES_NULL, EsGeneric data = ES_NULL) @todo(); // If you are copying lots of files, you can reuse the temporary copy buffer by storing the output copyBuffer; call EsHeapFree on it after the last copy.
function EsError EsFileControl(EsHandle file, EsFileControlFlags flags); function EsError EsFileControl(EsHandle file, EsFileControlOperation operation, const void *data, size_t dataBytes) @buffer_in(data, dataBytes);
function EsFileInformation EsFileOpen(STRING path, EsFileOpenFlags flags); function EsFileInformation EsFileOpen(STRING path, EsFileOpenFlags flags);
function EsFileOffset EsFileGetSize(EsHandle handle); function EsFileOffset EsFileGetSize(EsHandle handle);
function size_t EsFileReadSync(EsHandle file, EsFileOffset offset, size_t size, void *buffer) @buffer_out(buffer, size); function size_t EsFileReadSync(EsHandle file, EsFileOffset offset, size_t size, void *buffer) @buffer_out(buffer, size);
@ -2150,7 +2155,7 @@ function size_t EsPathFindUniqueName(char *buffer, size_t originalBytes, size_t
function EsError EsPathMove(STRING oldPath, STRING newPath, EsPathMoveFlags flags = ES_FLAGS_DEFAULT); function EsError EsPathMove(STRING oldPath, STRING newPath, EsPathMoveFlags flags = ES_FLAGS_DEFAULT);
function bool EsPathExists(STRING filePath, EsNodeType *type = ES_NULL) @out(type); // Returns true if the file/directory exists. function bool EsPathExists(STRING filePath, EsNodeType *type = ES_NULL) @out(type); // Returns true if the file/directory exists.
function EsError EsPathCreate(STRING filePath, EsNodeType type, bool createLeadingDirectories); function EsError EsPathCreate(STRING filePath, EsNodeType type, bool createLeadingDirectories);
function bool EsPathQueryInformation(STRING filePath, EsDirectoryChild *information) @out(information); function EsError EsPathQueryInformation(STRING filePath, EsDirectoryChild *information) @out(information);
function void *EsFileStoreReadAll(EsFileStore *file, size_t *fileSize) @heap_buffer_out(return, fileSize*) @out(fileSize); // Free with EsHeapFree. function void *EsFileStoreReadAll(EsFileStore *file, size_t *fileSize) @heap_buffer_out(return, fileSize*) @out(fileSize); // Free with EsHeapFree.
function bool EsFileStoreWriteAll(EsFileStore *file, const void *data, size_t dataBytes) @buffer_in(data, dataBytes); function bool EsFileStoreWriteAll(EsFileStore *file, const void *data, size_t dataBytes) @buffer_in(data, dataBytes);

View File

@ -9,7 +9,6 @@
// TODO Calling FSDirectoryEntryFound on all directory entries seen during scanning, even if they're not the target. // TODO Calling FSDirectoryEntryFound on all directory entries seen during scanning, even if they're not the target.
// TODO Informing the block cache when a directory is truncated and its extents are freed. // TODO Informing the block cache when a directory is truncated and its extents are freed.
// TODO Renaming directories does not work?
// TODO ESFS_CHECK_XXX are used to report out of memory errors, which shouldn't report KERNEL_PROBLEM_DAMAGED_FILESYSTEM. // TODO ESFS_CHECK_XXX are used to report out of memory errors, which shouldn't report KERNEL_PROBLEM_DAMAGED_FILESYSTEM.
#define ESFS_CHECK(x, y) if (!(x)) { KernelLog(LOG_ERROR, "EsFS", "damaged file system", y "\n"); return false; } #define ESFS_CHECK(x, y) if (!(x)) { KernelLog(LOG_ERROR, "EsFS", "damaged file system", y "\n"); return false; }
@ -342,6 +341,9 @@ static void Sync(KNode *_directory, KNode *node) {
// Get the most recent totalSize for the directory. // Get the most recent totalSize for the directory.
AttributeDirectory *directoryAttribute = (AttributeDirectory *) FindAttribute(&file->entry, ESFS_ATTRIBUTE_DIRECTORY); AttributeDirectory *directoryAttribute = (AttributeDirectory *) FindAttribute(&file->entry, ESFS_ATTRIBUTE_DIRECTORY);
directoryAttribute->totalSize = FSNodeGetTotalSize(node); directoryAttribute->totalSize = FSNodeGetTotalSize(node);
} else if (file->type == ES_NODE_FILE) {
// Get the most recent contentType for the file.
file->entry.contentType = FSNodeGetContentType(node);
} }
{ {
@ -422,6 +424,7 @@ static EsError Enumerate(KNode *node) {
} }
} else if (metadata.type == ES_NODE_FILE) { } else if (metadata.type == ES_NODE_FILE) {
metadata.totalSize = entry->fileSize; metadata.totalSize = entry->fileSize;
metadata.contentType = entry->contentType;
} }
EsError error = FSDirectoryEntryFound(node, &metadata, &reference, EsError error = FSDirectoryEntryFound(node, &metadata, &reference,
@ -1819,6 +1822,7 @@ static EsError Scan(const char *name, size_t nameLength, KNode *_directory) {
} }
} else { } else {
metadata.totalSize = entry->fileSize; metadata.totalSize = entry->fileSize;
metadata.contentType = entry->contentType;
} }
metadata.type = entry->nodeType == ESFS_NODE_TYPE_DIRECTORY ? ES_NODE_DIRECTORY : ES_NODE_FILE; metadata.type = entry->nodeType == ESFS_NODE_TYPE_DIRECTORY ? ES_NODE_DIRECTORY : ES_NODE_FILE;
@ -2009,6 +2013,7 @@ static void Register(KDevice *_parent) {
volume->rootDirectoryInitialChildren = rootDirectoryChildren; volume->rootDirectoryInitialChildren = rootDirectoryChildren;
volume->directoryEntryDataBytes = sizeof(DirectoryEntryReference); volume->directoryEntryDataBytes = sizeof(DirectoryEntryReference);
volume->nodeDataBytes = sizeof(FSNode); volume->nodeDataBytes = sizeof(FSNode);
volume->volumeFlags |= ES_VOLUME_STORES_CONTENT_TYPE;
FSRegisterBootFileSystem(volume, volume->superblock.osInstallation); FSRegisterBootFileSystem(volume, volume->superblock.osInstallation);
} }

View File

@ -67,7 +67,8 @@ EsError FSNodeDelete(KNode *node);
EsError FSNodeMove(KNode *node, KNode *destination, const char *newName, size_t nameNameBytes); EsError FSNodeMove(KNode *node, KNode *destination, const char *newName, size_t nameNameBytes);
EsError FSFileResize(KNode *node, EsFileOffset newSizeBytes); EsError FSFileResize(KNode *node, EsFileOffset newSizeBytes);
ptrdiff_t FSDirectoryEnumerate(KNode *node, K_USER_BUFFER EsDirectoryChild *buffer, size_t bufferSize); ptrdiff_t FSDirectoryEnumerate(KNode *node, K_USER_BUFFER EsDirectoryChild *buffer, size_t bufferSize);
EsError FSFileControl(KNode *node, uint32_t flags); EsError FSFileControlFlush(KNode *node);
EsError FSFileControlSetContentType(KNode *node, EsUniqueIdentifier identifier);
bool FSTrimCachedNode(MMObjectCache *); bool FSTrimCachedNode(MMObjectCache *);
bool FSTrimCachedDirectoryEntry(MMObjectCache *); bool FSTrimCachedDirectoryEntry(MMObjectCache *);
EsError FSBlockDeviceAccess(KBlockDeviceAccessRequest request); EsError FSBlockDeviceAccess(KBlockDeviceAccessRequest request);
@ -96,6 +97,10 @@ EsFileOffset FSNodeGetTotalSize(KNode *node) {
return node->directoryEntry->totalSize; return node->directoryEntry->totalSize;
} }
EsUniqueIdentifier FSNodeGetContentType(KNode *node) {
return node->directoryEntry->contentType;
}
char *FSNodeGetName(KNode *node, size_t *bytes) { char *FSNodeGetName(KNode *node, size_t *bytes) {
KWriterLockAssertLocked(&node->writerLock); KWriterLockAssertLocked(&node->writerLock);
*bytes = node->directoryEntry->item.key.longKeyBytes; *bytes = node->directoryEntry->item.key.longKeyBytes;
@ -381,42 +386,52 @@ ptrdiff_t FSFileWriteSync(KNode *node, K_USER_BUFFER const void *buffer, EsFileO
return error == ES_SUCCESS ? bytes : error; return error == ES_SUCCESS ? bytes : error;
} }
EsError FSFileControl(KNode *node, uint32_t flags) { EsError FSFileControlFlush(KNode *node) {
FSFile *file = (FSFile *) node; FSFile *file = (FSFile *) node;
if (flags & ES_FILE_CONTROL_FLUSH) { KWriterLockTake(&file->resizeLock, K_LOCK_EXCLUSIVE);
KWriterLockTake(&file->resizeLock, K_LOCK_EXCLUSIVE); EsDefer(KWriterLockReturn(&file->resizeLock, K_LOCK_EXCLUSIVE));
EsDefer(KWriterLockReturn(&file->resizeLock, K_LOCK_EXCLUSIVE));
CCSpaceFlush(&file->cache); CCSpaceFlush(&file->cache);
KWriterLockTake(&file->writerLock, K_LOCK_EXCLUSIVE); KWriterLockTake(&file->writerLock, K_LOCK_EXCLUSIVE);
EsDefer(KWriterLockReturn(&file->writerLock, K_LOCK_EXCLUSIVE)); EsDefer(KWriterLockReturn(&file->writerLock, K_LOCK_EXCLUSIVE));
__sync_fetch_and_and(&file->flags, ~NODE_MODIFIED); __sync_fetch_and_and(&file->flags, ~NODE_MODIFIED);
EsError error = FSFileCreateAndResizeOnFileSystem(file, file->directoryEntry->totalSize); EsError error = FSFileCreateAndResizeOnFileSystem(file, file->directoryEntry->totalSize);
if (error != ES_SUCCESS) return error; if (error != ES_SUCCESS) return error;
if (file->fileSystem->sync) { if (file->fileSystem->sync) {
// TODO Should we also sync the parent? // TODO Should we also sync the parent?
FSDirectory *parent = file->directoryEntry->parent; FSDirectory *parent = file->directoryEntry->parent;
if (parent) KWriterLockTake(&parent->writerLock, K_LOCK_EXCLUSIVE); if (parent) KWriterLockTake(&parent->writerLock, K_LOCK_EXCLUSIVE);
file->fileSystem->sync(parent, file); file->fileSystem->sync(parent, file);
if (parent) KWriterLockReturn(&parent->writerLock, K_LOCK_EXCLUSIVE); if (parent) KWriterLockReturn(&parent->writerLock, K_LOCK_EXCLUSIVE);
} }
if (file->error != ES_SUCCESS) { if (file->error != ES_SUCCESS) {
EsError error = file->error; EsError error = file->error;
file->error = ES_SUCCESS; file->error = ES_SUCCESS;
return error; return error;
}
} }
return ES_SUCCESS; return ES_SUCCESS;
} }
EsError FSFileControlSetContentType(KNode *node, EsUniqueIdentifier identifier) {
if (node->fileSystem->flags & ES_VOLUME_READ_ONLY) {
return ES_ERROR_FILE_ON_READ_ONLY_VOLUME;
} else if (~node->fileSystem->flags & ES_VOLUME_STORES_CONTENT_TYPE) {
return ES_ERROR_UNSUPPORTED;
} else {
node->directoryEntry->contentType = identifier;
__sync_fetch_and_or(&node->flags, NODE_MODIFIED); // Set the modified flag *after* making the modification.
return ES_SUCCESS;
}
}
////////////////////////////////////////// //////////////////////////////////////////
// Directories. // Directories.
////////////////////////////////////////// //////////////////////////////////////////
@ -669,6 +684,7 @@ void _FSDirectoryEnumerateVisit(AVLItem<FSDirectoryEntry> *item, K_USER_BUFFER E
output->fileSize = entry->totalSize; output->fileSize = entry->totalSize;
output->directoryChildren = entry->directoryChildren; output->directoryChildren = entry->directoryChildren;
output->nameBytes = nameBytes; output->nameBytes = nameBytes;
output->contentType = entry->contentType;
_FSDirectoryEnumerateVisit(item->children[0], buffer, bufferSize, position); _FSDirectoryEnumerateVisit(item->children[0], buffer, bufferSize, position);
_FSDirectoryEnumerateVisit(item->children[1], buffer, bufferSize, position); _FSDirectoryEnumerateVisit(item->children[1], buffer, bufferSize, position);
@ -808,9 +824,6 @@ EsError FSNodeCreate(FSDirectory *parent, const char *name, size_t nameBytes, Es
parent->directoryEntry->directoryChildren++; parent->directoryEntry->directoryChildren++;
} }
__sync_fetch_and_or(&parent->flags, NODE_MODIFIED);
__sync_fetch_and_or(&node->flags, NODE_MODIFIED);
// Only create directories immediately; files are created in FSWriteFromCache. // Only create directories immediately; files are created in FSWriteFromCache.
if (type != ES_NODE_FILE) { if (type != ES_NODE_FILE) {
@ -823,6 +836,10 @@ EsError FSNodeCreate(FSDirectory *parent, const char *name, size_t nameBytes, Es
} }
} }
// Set the modified flag *after* making the modification.
__sync_fetch_and_or(&parent->flags, NODE_MODIFIED);
__sync_fetch_and_or(&node->flags, NODE_MODIFIED);
// Put the node onto the object cache list. // Put the node onto the object cache list.
// Since the parent directory is locked, it should stay around for long enough to be immediately found FSNodeTraverseLayer. // Since the parent directory is locked, it should stay around for long enough to be immediately found FSNodeTraverseLayer.
if (node->flags & NODE_IN_CACHE_LIST) KernelPanic("FSNodeCreate - Node %x is already in the cache list.\n", node); if (node->flags & NODE_IN_CACHE_LIST) KernelPanic("FSNodeCreate - Node %x is already in the cache list.\n", node);
@ -1888,6 +1905,7 @@ void FSTrackUserFileSystemHandle(KDevice *device, bool opened) {
} }
void FSRegisterFileSystem(KFileSystem *fileSystem) { void FSRegisterFileSystem(KFileSystem *fileSystem) {
if (!fileSystem->write) fileSystem->volumeFlags |= ES_VOLUME_READ_ONLY;
fileSystem->trackHandle = FSTrackUserFileSystemHandle; fileSystem->trackHandle = FSTrackUserFileSystemHandle;
MMObjectCacheRegister(&fileSystem->cachedDirectoryEntries, FSTrimCachedDirectoryEntry, MMObjectCacheRegister(&fileSystem->cachedDirectoryEntries, FSTrimCachedDirectoryEntry,
sizeof(FSDirectoryEntry) + 16 /* approximate average name bytes */ + fileSystem->directoryEntryDataBytes); sizeof(FSDirectoryEntry) + 16 /* approximate average name bytes */ + fileSystem->directoryEntryDataBytes);

View File

@ -805,6 +805,7 @@ struct KNodeMetadata {
bool removingNodeFromCache, removingThisFromCache; bool removingNodeFromCache, removingThisFromCache;
EsFileOffset totalSize; EsFileOffset totalSize;
EsFileOffsetDifference directoryChildren; // ES_DIRECTORY_CHILDREN_UNKNOWN if not supported by the file system. EsFileOffsetDifference directoryChildren; // ES_DIRECTORY_CHILDREN_UNKNOWN if not supported by the file system.
EsUniqueIdentifier contentType;
}; };
struct KNode { struct KNode {
@ -840,6 +841,8 @@ struct KFileSystem : KDevice {
char name[64]; char name[64];
size_t nameBytes; size_t nameBytes;
EsVolumeFlags volumeFlags; // Note: ES_VOLUME_READ_ONLY will be automatically set if you don't set the write() callback pointer.
size_t directoryEntryDataBytes; // The size of the driverData passed to FSDirectoryEntryFound and received in the load callback. size_t directoryEntryDataBytes; // The size of the driverData passed to FSDirectoryEntryFound and received in the load callback.
size_t nodeDataBytes; // The average bytes allocated by the driver per node (used for managing cache sizes). size_t nodeDataBytes; // The average bytes allocated by the driver per node (used for managing cache sizes).
@ -906,6 +909,7 @@ struct KNodeInformation {
KNodeInformation FSNodeOpen(const char *path, size_t pathBytes, uint32_t flags, KNode *baseDirectory = nullptr); KNodeInformation FSNodeOpen(const char *path, size_t pathBytes, uint32_t flags, KNode *baseDirectory = nullptr);
EsFileOffset FSNodeGetTotalSize(KNode *node); EsFileOffset FSNodeGetTotalSize(KNode *node);
EsUniqueIdentifier FSNodeGetContentType(KNode *node);
char *FSNodeGetName(KNode *node, size_t *bytes); // For debugging use only. char *FSNodeGetName(KNode *node, size_t *bytes); // For debugging use only.

View File

@ -628,7 +628,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_VOLUME_GET_INFORMATION) {
information.spaceUsed = fileSystem->spaceUsed; information.spaceUsed = fileSystem->spaceUsed;
information.spaceTotal = fileSystem->spaceTotal; information.spaceTotal = fileSystem->spaceTotal;
information.id = fileSystem->objectID; information.id = fileSystem->objectID;
information.flags = fileSystem->write ? ES_FLAGS_DEFAULT : ES_VOLUME_READ_ONLY; information.flags = fileSystem->volumeFlags;
information.installationIdentifier = fileSystem->installationIdentifier; information.installationIdentifier = fileSystem->installationIdentifier;
information.identifier = fileSystem->identifier; information.identifier = fileSystem->identifier;
@ -702,6 +702,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_NODE_OPEN) {
information.type = _information.node->directoryEntry->type; information.type = _information.node->directoryEntry->type;
information.fileSize = _information.node->directoryEntry->totalSize; information.fileSize = _information.node->directoryEntry->totalSize;
information.directoryChildren = _information.node->directoryEntry->directoryChildren; information.directoryChildren = _information.node->directoryEntry->directoryChildren;
information.contentType = _information.node->directoryEntry->contentType;
information.handle = currentProcess->handleTable.OpenHandle(_information.node, flags, KERNEL_OBJECT_NODE); information.handle = currentProcess->handleTable.OpenHandle(_information.node, flags, KERNEL_OBJECT_NODE);
SYSCALL_WRITE(argument3, &information, sizeof(_EsNodeInformation)); SYSCALL_WRITE(argument3, &information, sizeof(_EsNodeInformation));
@ -1187,13 +1188,26 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_DIRECTORY_ENUMERATE) {
} }
SYSCALL_IMPLEMENT(ES_SYSCALL_FILE_CONTROL) { SYSCALL_IMPLEMENT(ES_SYSCALL_FILE_CONTROL) {
SYSCALL_HANDLE(argument0, KERNEL_OBJECT_NODE, file, KNode); SYSCALL_HANDLE_2(argument0, KERNEL_OBJECT_NODE, handle);
KNode *file = (KNode *) handle.object;
if (file->directoryEntry->type != ES_NODE_FILE) { if (file->directoryEntry->type != ES_NODE_FILE) {
SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_NODE_TYPE, true); SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_NODE_TYPE, true);
} else if ((handle.flags & (ES_FILE_WRITE_SHARED | ES_FILE_WRITE)) == 0) {
SYSCALL_RETURN(ES_FATAL_ERROR_INCORRECT_FILE_ACCESS, true);
} }
SYSCALL_RETURN(FSFileControl(file, argument1), false); EsError error = ES_ERROR_UNSUPPORTED;
if (argument1 == ES_FILE_CONTROL_FLUSH) {
error = FSFileControlFlush(file);
} else if (argument1 == ES_FILE_CONTROL_SET_CONTENT_TYPE) {
EsUniqueIdentifier identifier;
SYSCALL_READ(&identifier, argument2, sizeof(identifier));
error = FSFileControlSetContentType(file, identifier);
}
SYSCALL_RETURN(error, false);
} }
SYSCALL_IMPLEMENT(ES_SYSCALL_BATCH) { SYSCALL_IMPLEMENT(ES_SYSCALL_BATCH) {

View File

@ -111,9 +111,10 @@ typedef struct DirectoryEntry {
/* 32 */ uint64_t creationTime, accessTime, modificationTime; // Timekeeping. In microseconds since 1st January 1970. /* 32 */ uint64_t creationTime, accessTime, modificationTime; // Timekeeping. In microseconds since 1st January 1970.
/* 56 */ uint64_t fileSize; // The amount of data referenced by the data attribute in bytes. /* 56 */ uint64_t fileSize; // The amount of data referenced by the data attribute in bytes.
/* 64 */ EsUniqueIdentifier parent; // Identifier of the parent directory. /* 64 */ EsUniqueIdentifier parent; // Identifier of the parent directory.
/* 80 */ EsUniqueIdentifier contentType; // Identifier of the file content type.
#define ESFS_ATTRIBUTE_OFFSET (80) #define ESFS_ATTRIBUTE_OFFSET (96)
/* 80 */ uint8_t attributes[1024 - ESFS_ATTRIBUTE_OFFSET]; // Attribute list. /* 96 */ uint8_t attributes[1024 - ESFS_ATTRIBUTE_OFFSET]; // Attribute list.
} DirectoryEntry; } DirectoryEntry;
typedef struct GroupDescriptor { typedef struct GroupDescriptor {

View File

@ -59,6 +59,7 @@ FILE *systemLog;
char compilerPath[4096]; char compilerPath[4096];
int argc; int argc;
char **argv; char **argv;
bool runningTests;
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 1024 #define PATH_MAX 1024
@ -985,13 +986,17 @@ void BuildAndRun(int optimise, bool compile, int debug, int emulator, int log) {
Run(emulator, log, debug); Run(emulator, log, debug);
} }
exit(encounteredErrors ? 1 : 0); if (!runningTests) {
exit(encounteredErrors ? 1 : 0);
}
} }
void RunTests(int singleTest) { void RunTests(int singleTest) {
// TODO Capture emulator memory dump if a test causes a EsPanic. // TODO Capture emulator memory dump if a test causes a EsPanic.
// TODO Using SMP/KVM if available in the optimised test runs. // TODO Using SMP/KVM if available in the optimised test runs.
runningTests = true;
int successCount = 0, failureCount = 0; int successCount = 0, failureCount = 0;
CallSystem("mkdir -p root/Essence/Settings/API\\ Tests"); CallSystem("mkdir -p root/Essence/Settings/API\\ Tests");
FILE *testFailures = fopen("bin/Logs/Test Failures.txt", "wb"); FILE *testFailures = fopen("bin/Logs/Test Failures.txt", "wb");

View File

@ -15,6 +15,10 @@ str compilerPath #persist;
int compilerIndex #persist; int compilerIndex #persist;
bool runningMakefiles #persist; bool runningMakefiles #persist;
/////////////////////////////////////////////////////////////
// Environment setup
/////////////////////////////////////////////////////////////
void Setup(bool forAutomation) { void Setup(bool forAutomation) {
assert PersistRead("bin/start.script.persist"); assert PersistRead("bin/start.script.persist");
@ -109,6 +113,10 @@ void Setup(bool forAutomation) {
} }
} }
/////////////////////////////////////////////////////////////
// Command interface
/////////////////////////////////////////////////////////////
void DoCommand(str command) { void DoCommand(str command) {
if command == "line-count" { if command == "line-count" {
int count = 0; int count = 0;
@ -215,6 +223,10 @@ void Start() {
} }
} }
/////////////////////////////////////////////////////////////
// Automation scripts
/////////////////////////////////////////////////////////////
void GenerateOVF() { void GenerateOVF() {
str[] template = StringSplitByCharacter(FileReadAll("util/template.ovf"):assert(), "$", true); str[] template = StringSplitByCharacter(FileReadAll("util/template.ovf"):assert(), "$", true);
assert template:len() == 5; assert template:len() == 5;