diff --git a/CMakeLists.txt b/CMakeLists.txt index 4840897650..071c10c9eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,8 @@ else() endif() set(PLUGIN_GENERATED_FILES - ${GENERATED_SOURCE_DIR}/generated/plugins/src/FilePlugin/FilePlugin.c) + ${GENERATED_SOURCE_DIR}/generated/plugins/src/FilePlugin/FilePlugin.c + ${GENERATED_SOURCE_DIR}/generated/plugins/src/NewFilePlugin/NewFilePlugin.c) if(FLAVOUR MATCHES "CoInterpreter") diff --git a/cmake/plugins.cmake b/cmake/plugins.cmake index d072199fca..db1f56cc44 100644 --- a/cmake/plugins.cmake +++ b/cmake/plugins.cmake @@ -8,6 +8,8 @@ if(WIN) target_compile_definitions(FilePlugin PRIVATE "-DWIN32_FILE_SUPPORT") endif() +add_vm_plugin(NewFilePlugin TRUE TRUE) + add_vm_plugin(FileAttributesPlugin FALSE TRUE) target_link_libraries(FileAttributesPlugin PRIVATE FilePlugin) diff --git a/cmake/versionExtraction.cmake b/cmake/versionExtraction.cmake index 82576e3987..da05ba4543 100644 --- a/cmake/versionExtraction.cmake +++ b/cmake/versionExtraction.cmake @@ -41,7 +41,7 @@ endif() if (GIT_FOUND AND VERSION_UPDATE_FROM_GIT) # Get last tag from git - execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags + execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags --always WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/cmake/vmmaker.cmake b/cmake/vmmaker.cmake index 4b90919adb..0137975124 100644 --- a/cmake/vmmaker.cmake +++ b/cmake/vmmaker.cmake @@ -40,6 +40,7 @@ endif() set(PLUGIN_GENERATED_FILES ${PHARO_CURRENT_GENERATED}/plugins/src/FilePlugin/FilePlugin.c + ${PHARO_CURRENT_GENERATED}/plugins/src/NewFilePlugin/NewFilePlugin.c ${PHARO_CURRENT_GENERATED}/plugins/src/SurfacePlugin/SurfacePlugin.c ${PHARO_CURRENT_GENERATED}/plugins/src/FloatArrayPlugin/FloatArrayPlugin.c) diff --git a/plugins/NewFilePlugin/include/common/NewFile.h b/plugins/NewFilePlugin/include/common/NewFile.h new file mode 100644 index 0000000000..cfa8aae364 --- /dev/null +++ b/plugins/NewFilePlugin/include/common/NewFile.h @@ -0,0 +1,143 @@ +#ifndef PHARO_NEWFILE_H +#define PHARO_NEWFILE_H + +#include +#include +#include + +#define PHARO_NEWFILE_EXPORT + +typedef struct NewDirectory_s NewDirectory_t; +typedef struct NewFile_s NewFile_t; + +typedef enum NewFileOpenMode_e +{ + NewFileOpenModeReadOnly = 0, + NewFileOpenModeWriteOnly, + NewFileOpenModeReadWrite, +} NewFileOpenMode_t; + +typedef enum NewFileCreationDisposition_e +{ + NewFileCreationDispositionCreateNew = 1, + NewFileCreationDispositionCreateAlways, + NewFileCreationDispositionOpenExisting, + NewFileCreationDispositionOpenAlways, + NewFileCreationDispositionTruncateExisting, +} NewFileCreationDisposition_t; + +typedef enum NewFileOpenFlags_e +{ + NewFileOpenFlagsNone = 0, + NewFileOpenFlagsAppend = 1<<0, +} NewFileOpenFlags_t; + +typedef enum NewFileSeekMode_e +{ + NewFileSeekModeSet = 0, + NewFileSeekModeCurrent, + NewFileSeekModeEnd, +} NewFileSeekMode_t; + +typedef enum NewFileMemMapProtection_e +{ + NewFileMemMapProtectionReadOnly = 0, + NewFileMemMapProtectionReadWrite, +} NewFileMemMapProtection_t; + +/** + * Creates a directory. + */ +PHARO_NEWFILE_EXPORT bool NewDirectory_create(const char *path, size_t pathSize); + +/** + * Removes an empty directory + */ +PHARO_NEWFILE_EXPORT bool NewDirectory_removeEmpty(const char *path, size_t pathSize); + +/** + * Opens a directory. + */ +PHARO_NEWFILE_EXPORT NewDirectory_t *NewDirectory_open(const char *path, size_t pathSize); + +/** + * Rewinds a directory. + */ +PHARO_NEWFILE_EXPORT bool NewDirectory_rewind(NewDirectory_t *directory); + +/** + * Gets the next entry. + */ +PHARO_NEWFILE_EXPORT const char *NewDirectory_next(NewDirectory_t *directory); + +/** + * Closes a directory + */ +PHARO_NEWFILE_EXPORT void NewDirectory_close(NewDirectory_t *directory); + +/** + * Deletes a file + */ +PHARO_NEWFILE_EXPORT bool NewFile_deleteFile(const char *path, size_t pathSize); + +/** + * Opens a file. + */ +PHARO_NEWFILE_EXPORT NewFile_t *NewFile_open(const char *path, size_t pathSize, NewFileOpenMode_t mode, NewFileCreationDisposition_t creationDisposition, NewFileOpenFlags_t flags); + +/*+ + * Closes the file + */ +PHARO_NEWFILE_EXPORT void NewFile_close(NewFile_t *file); + +/*+ + * Get file size + */ +PHARO_NEWFILE_EXPORT int64_t NewFile_getSize(NewFile_t *file); + +/*+ + * Seeks + */ +PHARO_NEWFILE_EXPORT void NewFile_seek(NewFile_t *file, int64_t offset, NewFileSeekMode_t seekMode); + +/** + * Tell the current file position. + */ +PHARO_NEWFILE_EXPORT int64_t NewFile_tell(NewFile_t *file); + +/** + * Sets the file size. + */ +PHARO_NEWFILE_EXPORT bool NewFile_truncate(NewFile_t *file, uint64_t newFileSize); + +/** + * Read + */ +PHARO_NEWFILE_EXPORT int64_t NewFile_read(NewFile_t *file, void * buffer, size_t bufferOffset, size_t readSize); + +/** + * Write + */ +PHARO_NEWFILE_EXPORT int64_t NewFile_write(NewFile_t *file, const void * buffer, size_t bufferOffset, size_t writeSize); + +/** + * Read at offset + */ +PHARO_NEWFILE_EXPORT int64_t NewFile_readAtOffset(NewFile_t *file, void * buffer, size_t bufferOffset, size_t readSize, uint64_t offset); + +/** + * Write + */ +PHARO_NEWFILE_EXPORT int64_t NewFile_writeAtOffset(NewFile_t *file, const void * buffer, size_t bufferOffset, size_t writeSize, uint64_t offset); + +/** + * Memory Map + */ +PHARO_NEWFILE_EXPORT void * NewFile_memoryMap(NewFile_t *file, NewFileMemMapProtection_t protection); + +/** + * Memory Unmap + */ +PHARO_NEWFILE_EXPORT void NewFile_memoryUnmap(NewFile_t *file); + +#endif // PHARO_NEWFILE_H diff --git a/plugins/NewFilePlugin/include/common/NewFilePlugin.h b/plugins/NewFilePlugin/include/common/NewFilePlugin.h new file mode 100644 index 0000000000..272827e1a4 --- /dev/null +++ b/plugins/NewFilePlugin/include/common/NewFilePlugin.h @@ -0,0 +1,8 @@ +#ifndef NEW_FILE_PLUGIN_H +#define NEW_FILE_PLUGIN_H + +#pragma once + +#include "NewFile.h" + +#endif //NEW_FILE_PLUGIN_H diff --git a/plugins/NewFilePlugin/src/unix/UnixFile.c b/plugins/NewFilePlugin/src/unix/UnixFile.c new file mode 100644 index 0000000000..bb7a747b00 --- /dev/null +++ b/plugins/NewFilePlugin/src/unix/UnixFile.c @@ -0,0 +1,306 @@ +#include "NewFile.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct NewDirectory_s +{ + DIR *handle; +}; + +static char * +makeCStringWithFixedString(const char *string, size_t stringSize) +{ + char *cstring = malloc(stringSize + 1); + memcpy(cstring, string, stringSize); + cstring[stringSize] = 0; + return cstring; +} + +bool +NewDirectory_create(const char *path, size_t pathSize) +{ + char *cpath = makeCStringWithFixedString(path, pathSize); + bool result = mkdir(cpath, 0755) == 0; + free(cpath); + return result; +} + +bool NewDirectory_removeEmpty(const char *path, size_t pathSize) +{ + char *cpath = makeCStringWithFixedString(path, pathSize); + bool result = rmdir(cpath) == 0; + free(cpath); + return result; +} + +NewDirectory_t * +NewDirectory_open(const char *path, size_t pathSize) +{ + char *cpath = makeCStringWithFixedString(path, pathSize); + DIR *handle = opendir(path); + free(cpath); + if(!handle) + return NULL; + + NewDirectory_t *directory = calloc(1, sizeof(NewDirectory_t)); + directory->handle = handle; + return directory; +} + +bool +NewDirectory_rewind(NewDirectory_t *directory) +{ + if(!directory) + return false; + + rewinddir(directory->handle); + return true; +} + +const char * +NewDirectory_next(NewDirectory_t *directory) +{ + if(!directory) + return NULL; + + struct dirent *entry = readdir(directory->handle); + if(!entry) + return NULL; + + return entry->d_name; +} + +void +NewDirectory_close(NewDirectory_t *directory) +{ + if(!directory) + return; + + closedir(directory->handle); + free(directory); +} + +struct NewFile_s +{ + int fileDescriptor; + int memoryMapCount; + size_t memoryMapLength; + void *memoryMapAddress; +}; + +PHARO_NEWFILE_EXPORT bool +NewFile_deleteFile(const char *path, size_t pathSize) +{ + char *cpath = makeCStringWithFixedString(path, pathSize); + bool result = unlink(cpath) == 0; + free(cpath); + return result; +} + +NewFile_t * +NewFile_open(const char *path, size_t pathSize, NewFileOpenMode_t mode, NewFileCreationDisposition_t creationDisposition, NewFileOpenFlags_t flags) +{ + int openFlags = 0; + int openMode = 0644; + switch(mode) + { + case NewFileOpenModeReadOnly: + openFlags = O_RDONLY; + break; + case NewFileOpenModeWriteOnly: + openFlags = O_WRONLY; + break; + case NewFileOpenModeReadWrite: + openFlags = O_RDWR; + break; + default: + return NULL; + } + + if(flags & NewFileOpenFlagsAppend) + openFlags |= O_APPEND; + + switch(creationDisposition) + { + case NewFileCreationDispositionCreateNew: + openFlags |= O_CREAT | O_EXCL; + break; + case NewFileCreationDispositionCreateAlways: + openFlags |= O_CREAT | O_TRUNC; + break; + case NewFileCreationDispositionOpenExisting: + break; + case NewFileCreationDispositionOpenAlways: + openFlags |= O_CREAT; + break; + case NewFileCreationDispositionTruncateExisting: + openFlags |= O_TRUNC; + break; + default: + break; + } + + char *cpath = makeCStringWithFixedString(path, pathSize); + int fd = open(cpath, openFlags, openMode); + free(cpath); + if(fd < 0) + return NULL; + + NewFile_t *newFile = calloc(1, sizeof(NewFile_t)); + newFile->fileDescriptor = fd; + return newFile; +} + +void +NewFile_close(NewFile_t *file) +{ + if(!file) + return; + + close(file->fileDescriptor); + free(file); +} + +int64_t +NewFile_getSize(NewFile_t *file) +{ + if(!file) + return -1; + + struct stat s; + if(fstat(file->fileDescriptor, &s)) + return -1; + + return s.st_size; +} + +void NewFile_seek(NewFile_t *file, int64_t offset, NewFileSeekMode_t seekMode) +{ + int whence = 0; + switch(seekMode) + { + case NewFileSeekModeSet: + whence = SEEK_SET; + break; + case NewFileSeekModeCurrent: + whence = SEEK_CUR; + break; + case NewFileSeekModeEnd: + whence = SEEK_END; + break; + default: + return; + } + + lseek(file->fileDescriptor, offset, whence); +} + +int64_t +NewFile_tell(NewFile_t *file) +{ + if(!file) + return 0; + return lseek(file->fileDescriptor, 0, SEEK_CUR); +} + +bool +NewFile_truncate(NewFile_t *file, uint64_t newFileSize) +{ + if(!file) + return false; + + return ftruncate(file->fileDescriptor, newFileSize) == 0; +} + +int64_t +NewFile_read(NewFile_t *file, void * buffer, size_t bufferOffset, size_t readSize) +{ + if(!file) + return -1; + + return read(file->fileDescriptor, (char*)buffer + bufferOffset, readSize); +} + +int64_t +NewFile_write(NewFile_t *file, const void * buffer, size_t bufferOffset, size_t writeSize) +{ + if(!file) + return -1; + + return write(file->fileDescriptor, (const char*)buffer + bufferOffset, writeSize); +} + +int64_t +NewFile_readAtOffset(NewFile_t *file, void * buffer, size_t bufferOffset, size_t readSize, uint64_t offset) +{ + if(!file) + return -1; + + return pread(file->fileDescriptor, (char*)buffer + bufferOffset, readSize, offset); +} + +int64_t +NewFile_writeAtOffset(NewFile_t *file, const void * buffer, size_t bufferOffset, size_t writeSize, uint64_t offset) +{ + if(!file) + return -1; + + return pwrite(file->fileDescriptor, (const char*)buffer + bufferOffset, writeSize, offset); +} + +PHARO_NEWFILE_EXPORT void * +NewFile_memoryMap(NewFile_t *file, NewFileMemMapProtection_t protection) +{ + if(!file) + return NULL; + + if(file->memoryMapCount > 0) + { + ++file->memoryMapCount; + return file->memoryMapAddress; + } + + file->memoryMapLength = NewFile_getSize(file); + if(file->memoryMapLength == 0) + return 0; + + int mmapProtection = PROT_NONE; + switch(protection) + { + case NewFileMemMapProtectionReadOnly: + mmapProtection = PROT_READ; + break; + case NewFileMemMapProtectionReadWrite: + mmapProtection = PROT_READ | PROT_WRITE; + break; + default: + break; + } + + file->memoryMapAddress = mmap(NULL, file->memoryMapLength, mmapProtection, MAP_SHARED, file->fileDescriptor, 0); + if(file->memoryMapAddress == MAP_FAILED) + return NULL; + + ++file->memoryMapCount; + return file->memoryMapAddress; +} + +PHARO_NEWFILE_EXPORT void +NewFile_memoryUnmap(NewFile_t *file) +{ + if(!file || file->memoryMapCount <= 0) + return; + + --file->memoryMapCount; + if(!file->memoryMapCount) + return; + + munmap(file->memoryMapAddress, file->memoryMapLength); +} diff --git a/plugins/NewFilePlugin/src/win/Win32File.c b/plugins/NewFilePlugin/src/win/Win32File.c new file mode 100644 index 0000000000..4a36b6c31c --- /dev/null +++ b/plugins/NewFilePlugin/src/win/Win32File.c @@ -0,0 +1,479 @@ +#include "NewFile.h" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include + +#include + +struct NewDirectory_s +{ + WCHAR *path; + HANDLE findHandle; + WIN32_FIND_DATAW findData; + bool isFirst; + char *elementName; +}; + +struct NewFile_s +{ + HANDLE fileHandle; + int memoryMapCount; + HANDLE memoryMapHandle; + void *memoyMapAddress; +}; + +static WCHAR * +NewFile_utf8ToWideChar(const char *string, size_t stringLen) +{ + int wstringSize = MultiByteToWideChar(CP_UTF8, 0, string, (int)stringLen, NULL, 0); + if(wstringSize <= 0) + return NULL; + + WCHAR *wstring = calloc(wstringSize + 1, sizeof(WCHAR)); + wstringSize = MultiByteToWideChar(CP_UTF8, 0, string, (int)stringLen, wstring, wstringSize + 1); + if(wstringSize <= 0) + { + free(wstring); + return NULL; + } + + return wstring; +} + +static char * +NewFile_wideCharToUtf8(const WCHAR *wstring) +{ + int characterCount = WideCharToMultiByte(CP_UTF8, 0, wstring, -1, NULL, 0, NULL, NULL); + char *string = calloc(characterCount + 1, 1); + WideCharToMultiByte(CP_UTF8, 0, wstring, -1, string, characterCount, NULL, NULL); + + return string; +} + +static bool +NewFile_isAbsolutePath(const char *path, size_t pathSize) +{ + return pathSize >= 3 && (path[1] == ':') && (path[2] == '\\'); +} + +static WCHAR * +NewFile_preparePath(const char *path, size_t pathSize) +{ + // Add the \\?\ prefix to absolute paths + if(NewFile_isAbsolutePath(path, pathSize)) + { + size_t pathLength = strlen(path); + char *stringWithPrefix = calloc(4 + pathLength, 1); + memcpy(stringWithPrefix, "\\\\?\\", 4); + memcpy(stringWithPrefix + 4, path, pathLength); + + WCHAR *wpath = NewFile_utf8ToWideChar(stringWithPrefix, 4 + pathLength); + free(stringWithPrefix); + return wpath; + } + + return NewFile_utf8ToWideChar(path, pathSize); +} + +static WCHAR * +NewFile_prepareWildCardPath(const char *path, size_t pathSize) +{ + const char * suffix = "\\*"; + size_t pathLength = pathSize; + size_t suffixLength = strlen(suffix); + + char *pathWithWildCard = calloc(pathLength + suffixLength + 1, 1); + memcpy(pathWithWildCard, path, pathLength); + memcpy(pathWithWildCard + pathLength, suffix, suffixLength); + + WCHAR *wpath = NewFile_preparePath(pathWithWildCard, pathLength + suffixLength); + free(pathWithWildCard); + return wpath; +} + +/** + * Creates a directory. + */ +PHARO_NEWFILE_EXPORT bool +NewDirectory_create(const char *path, size_t pathSize) +{ + WCHAR *wpath = NewFile_preparePath(path, pathSize); + BOOL result = CreateDirectoryW(wpath, NULL); + free(wpath); + return result; +} + +/** + * Removes an empty directory + */ +PHARO_NEWFILE_EXPORT bool +NewDirectory_removeEmpty(const char *path, size_t pathSize) +{ + WCHAR *wpath = NewFile_preparePath(path, pathSize); + BOOL result = RemoveDirectoryW(wpath); + free(wpath); + return result; +} + +PHARO_NEWFILE_EXPORT NewDirectory_t* +NewDirectory_open(const char *path, size_t pathSize) +{ + if(!path) + return NULL; + + WCHAR *wpath = NewFile_prepareWildCardPath(path, pathSize); + + WIN32_FIND_DATAW findData; + HANDLE findHandle = FindFirstFileW(wpath, &findData); + if(findHandle == INVALID_HANDLE_VALUE) + { + free(wpath); + return NULL; + } + + NewDirectory_t *directory = calloc(1, sizeof(NewDirectory_t)); + directory->path = wpath; + directory->findHandle = findHandle; + directory->findData = findData; + directory->isFirst = true; + directory->elementName = NewFile_wideCharToUtf8(directory->findData.cFileName); + return directory; +} + +PHARO_NEWFILE_EXPORT bool +NewDirectory_rewind(NewDirectory_t *directory) +{ + if(!directory || directory->isFirst) + return true; + + WIN32_FIND_DATAW newFindData; + HANDLE newFindHandle = FindFirstFileW(directory->path, &newFindData); + if(newFindHandle == INVALID_HANDLE_VALUE) + return false; + + FindClose(directory->findHandle); + directory->findHandle = newFindHandle; + directory->findData = newFindData; + if(directory->elementName) + free(directory->elementName); + + directory->elementName = NewFile_wideCharToUtf8(directory->findData.cFileName); + return true; +} + +PHARO_NEWFILE_EXPORT const char * +NewDirectory_next(NewDirectory_t *directory) +{ + if(!directory) + return NULL; + + if(directory->isFirst) + { + directory->isFirst = false; + return directory->elementName; + } + + free(directory->elementName); + directory->elementName = NULL; + + if(!FindNextFileW(directory->findHandle, &directory->findData)) + return NULL; + + directory->elementName = NewFile_wideCharToUtf8(directory->findData.cFileName); + return directory->elementName; +} + +PHARO_NEWFILE_EXPORT void +NewDirectory_close(NewDirectory_t *directory) +{ + if(!directory) + return; + + FindClose(directory->findHandle); + free(directory->elementName); + free(directory->path); + free(directory); +} + +PHARO_NEWFILE_EXPORT bool +NewFile_deleteFile(const char *path, size_t pathSize) +{ + WCHAR *wpath = NewFile_preparePath(path, pathSize); + if(!wpath) + return false; + + bool result = DeleteFileW(wpath); + free(wpath); + return result; +} + +PHARO_NEWFILE_EXPORT NewFile_t * +NewFile_open(const char *path, size_t pathSize, NewFileOpenMode_t mode, NewFileCreationDisposition_t creationDisposition, NewFileOpenFlags_t flags) +{ + DWORD desiredAccess = 0; + DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + switch(mode) + { + case NewFileOpenModeReadOnly: + desiredAccess = GENERIC_READ; + break; + case NewFileOpenModeWriteOnly: + desiredAccess = GENERIC_WRITE; + break; + case NewFileOpenModeReadWrite: + desiredAccess = GENERIC_READ | GENERIC_WRITE; + break; + default: + return NULL; + } + + DWORD dwCreationDisposition; + switch(creationDisposition) + { + case NewFileCreationDispositionCreateNew: + dwCreationDisposition = CREATE_NEW; + break; + case NewFileCreationDispositionCreateAlways: + dwCreationDisposition = CREATE_ALWAYS; + break; + case NewFileCreationDispositionOpenExisting: + dwCreationDisposition = OPEN_EXISTING; + break; + case NewFileCreationDispositionOpenAlways: + dwCreationDisposition = OPEN_ALWAYS; + break; + case NewFileCreationDispositionTruncateExisting: + dwCreationDisposition = TRUNCATE_EXISTING; + break; + default: + return NULL; + } + + WCHAR *wpath = NewFile_preparePath(path, pathSize); + if(!wpath) + return NULL; + + HANDLE handle = CreateFileW(wpath, desiredAccess, shareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + free(wpath); + if(handle == INVALID_HANDLE_VALUE) + return NULL; + + NewFile_t *newFile = calloc(1, sizeof(NewFile_t)); + newFile->fileHandle = handle; + return newFile; +} + +PHARO_NEWFILE_EXPORT void +NewFile_close(NewFile_t *file) +{ + if(!file) + return; + + if(file->memoryMapCount > 0) + { + UnmapViewOfFile(file->memoyMapAddress); + CloseHandle(file->memoryMapHandle); + } + CloseHandle(file->fileHandle); + free(file); +} + +PHARO_NEWFILE_EXPORT int64_t +NewFile_getSize(NewFile_t *file) +{ + if(!file) + return -1; + + LARGE_INTEGER fileSize = {{0}}; + if(!GetFileSizeEx(file->fileHandle, &fileSize)) + return -1; + + return fileSize.QuadPart; +} + +PHARO_NEWFILE_EXPORT void +NewFile_seek(NewFile_t *file, int64_t offset, NewFileSeekMode_t seekMode) +{ + if(!file) + return; + + LARGE_INTEGER largeIntegerOffset = {{0}}; + largeIntegerOffset.QuadPart = offset; + + DWORD moveMethod; + switch(seekMode) + { + case NewFileSeekModeSet: + moveMethod = FILE_BEGIN; + break; + case NewFileSeekModeCurrent: + moveMethod = FILE_CURRENT; + break; + case NewFileSeekModeEnd: + moveMethod = FILE_END; + break; + default: + abort(); + } + + SetFilePointerEx(file->fileHandle, largeIntegerOffset, NULL, moveMethod); +} + +PHARO_NEWFILE_EXPORT int64_t +NewFile_tell(NewFile_t *file) +{ + if(!file) + return 0; + + LARGE_INTEGER largeIntegerOffset = {{0}}; + LARGE_INTEGER filePointerPosition = {{0}}; + + if(!SetFilePointerEx(file->fileHandle, largeIntegerOffset, &filePointerPosition, FILE_CURRENT)) + return -1; + return filePointerPosition.QuadPart; +} + +PHARO_NEWFILE_EXPORT bool +NewFile_truncate(NewFile_t *file, uint64_t newFileSize) +{ + if(!file) + return false; + + LARGE_INTEGER largeIntegerOffset = {{0}}; + largeIntegerOffset.QuadPart = newFileSize; + LARGE_INTEGER oldFileOffset = {{0}}; + + // Move the file pointer to the new ending. + if(!SetFilePointerEx(file->fileHandle, largeIntegerOffset, &oldFileOffset, FILE_BEGIN)) + return -1; + + // Set the new end of file. + bool result = SetEndOfFile(file->fileHandle); + + // Restore the file pointer. + if(SetFilePointerEx(file->fileHandle, oldFileOffset, NULL, FILE_BEGIN)) + return -1; + + return result; +} + +PHARO_NEWFILE_EXPORT int64_t +NewFile_read(NewFile_t *file, void * buffer, size_t bufferOffset, size_t readSize) +{ + if(!file) + return -1; + + DWORD readBytes = 0; + if(!ReadFile(file->fileHandle, (char*)buffer + bufferOffset, (DWORD)readSize, &readBytes, NULL)) + return -1; + return readBytes; +} + +PHARO_NEWFILE_EXPORT int64_t +NewFile_write(NewFile_t *file, const void * buffer, size_t bufferOffset, size_t writeSize) +{ + if(!file) + return -1; + + DWORD writtenBytes = 0; + if(!WriteFile(file->fileHandle, (const char*)buffer + bufferOffset, (DWORD)writeSize, &writtenBytes, NULL)) + return -1; + + return writtenBytes; +} + +PHARO_NEWFILE_EXPORT int64_t +NewFile_readAtOffset(NewFile_t *file, void * buffer, size_t bufferOffset, size_t readSize, uint64_t offset) +{ + if(!file) + return -1; + + OVERLAPPED overlapped = {0}; + overlapped.Offset = (DWORD)offset; + overlapped.OffsetHigh = (DWORD)(offset >> 32); + + DWORD readBytes = 0; + if(!ReadFile(file->fileHandle, (char*)buffer + bufferOffset, (DWORD)readSize, &readBytes, &overlapped)) + return -1; + return readBytes; +} + +PHARO_NEWFILE_EXPORT int64_t +NewFile_writeAtOffset(NewFile_t *file, const void * buffer, size_t bufferOffset, size_t writeSize, uint64_t offset) +{ + if(!file) + return -1; + + OVERLAPPED overlapped = {0}; + overlapped.Offset = (DWORD)offset; + overlapped.OffsetHigh = (DWORD)(offset >> 32); + + DWORD writtenBytes = 0; + if(!WriteFile(file->fileHandle, (const char*)buffer + bufferOffset, (DWORD)writeSize, &writtenBytes, &overlapped)) + return -1; + + return writtenBytes; +} + +PHARO_NEWFILE_EXPORT void * NewFile_memoryMap(NewFile_t *file, NewFileMemMapProtection_t protection) +{ + if(!file) + return NULL; + + if(file->memoryMapCount) + { + // TODO: Support changing the permission + ++file->memoryMapCount; + return file->memoyMapAddress; + } + + // Map the file protection + DWORD flProtect = 0; + DWORD desiredAccess = 0; + switch(protection) + { + case NewFileMemMapProtectionReadOnly: + flProtect = PAGE_READONLY; + desiredAccess = FILE_MAP_READ; + break; + case NewFileMemMapProtectionReadWrite: + flProtect = PAGE_READWRITE; + desiredAccess = FILE_MAP_WRITE; + break; + } + + file->memoryMapHandle = CreateFileMappingA(file->fileHandle, NULL, flProtect, 0, 0, NULL); + if(!file->memoryMapHandle) + return NULL; + + file->memoyMapAddress = MapViewOfFile(file->memoryMapHandle, desiredAccess, 0, 0, 0); + if(!file->memoyMapAddress) + { + CloseHandle(file->memoryMapHandle); + return NULL; + } + + file->memoryMapCount = 1; + return file->memoyMapAddress; +} + +PHARO_NEWFILE_EXPORT void +NewFile_memoryUnmap(NewFile_t *file) +{ + if(!file || file->memoryMapCount == 0) + return; + + --file->memoryMapCount; + if(file->memoryMapCount > 0) + return; + + UnmapViewOfFile(file->memoyMapAddress); + CloseHandle(file->memoryMapHandle); + file->memoryMapHandle = 0; +} diff --git a/smalltalksrc/VMMaker/NewFilePlugin.class.st b/smalltalksrc/VMMaker/NewFilePlugin.class.st new file mode 100644 index 0000000000..aa2591226d --- /dev/null +++ b/smalltalksrc/VMMaker/NewFilePlugin.class.st @@ -0,0 +1,382 @@ +" +I provide file system access via the new file library. +" +Class { + #name : 'NewFilePlugin', + #superclass : 'InterpreterPlugin', + #category : 'VMMaker-Plugins', + #package : 'VMMaker', + #tag : 'Plugins' +} + +{ #category : 'testing' } +NewFilePlugin class >> hasHeaderFile [ + "If there is a single intrinsic header file to be associated with the plugin, here is where you want to flag" + ^true +] + +{ #category : 'testing' } +NewFilePlugin class >> requiresCrossPlatformFiles [ + "this plugin requires cross platform files in order to work" + ^true +] + +{ #category : 'testing' } +NewFilePlugin class >> requiresPlatformFiles [ + "this plugin requires platform specific files in order to work" + ^true +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveDeleteFile [ + + + + | pathPointer pathIndex result pathSize | + pathPointer := interpreterProxy stackValue: 0. + (interpreterProxy isBytes: pathPointer) + ifFalse: [^ interpreterProxy primitiveFail]. + pathIndex := interpreterProxy firstIndexableField: pathPointer. + pathSize := interpreterProxy byteSizeOf: pathPointer. + + result := self cCode: 'NewFile_deleteFile(pathIndex, pathSize)'. + + interpreterProxy failed + ifFalse: [interpreterProxy methodReturnBool: result] + +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveDirectoryClose [ + + + + | directoryOop directoryPointer | + directoryOop := interpreterProxy stackValue: 0. + interpreterProxy failed ifFalse: [ + directoryPointer := self pointerAtPointer: (interpreterProxy firstIndexableField: directoryOop). + self cCode: 'NewDirectory_close(directoryPointer)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy pop: 1 "pop directory; leave rcvr on stack" ]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveDirectoryCreate [ + + + + | pathPointer pathIndex result pathSize | + pathPointer := interpreterProxy stackValue: 0. + (interpreterProxy isBytes: pathPointer) + ifFalse: [^ interpreterProxy primitiveFail]. + pathIndex := interpreterProxy firstIndexableField: pathPointer. + pathSize := interpreterProxy byteSizeOf: pathPointer. + + result := self cCode: 'NewDirectory_create(pathIndex, pathSize)'. + + interpreterProxy failed + ifFalse: [interpreterProxy methodReturnBool: result] + +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveDirectoryNext [ + + + + + | directoryOop directoryPointer stringOop stringPointer| + directoryOop := interpreterProxy stackValue: 0. + stringOop := interpreterProxy instantiateClass: interpreterProxy classExternalAddress indexableSize: BytesPerWord. + interpreterProxy failed ifFalse: [ + directoryPointer := self pointerAtPointer: (interpreterProxy firstIndexableField: directoryOop). + stringPointer := self cCode: 'NewDirectory_next(directoryPointer)'. + self pointerAtPointer: (interpreterProxy firstIndexableField: stringOop) put: stringPointer. + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: stringOop ]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveDirectoryOpen [ + + + +"primitiveOpen: path mode: mode creationDisposition: creationDisposition flags: flags + " + | pathPointer pathIndex directoryPointer directoryOop pathSize | + pathPointer := interpreterProxy stackValue: 0. + (interpreterProxy isBytes: pathPointer) + ifFalse: [^ interpreterProxy primitiveFail]. + pathIndex := interpreterProxy firstIndexableField: pathPointer. + pathSize := interpreterProxy byteSizeOf: pathPointer. + + directoryOop := interpreterProxy instantiateClass: interpreterProxy classExternalAddress indexableSize: BytesPerWord. + + directoryPointer := self cCode: 'NewDirectory_open(pathIndex, pathSize)'. + self pointerAtPointer: (interpreterProxy firstIndexableField: directoryOop) put: directoryPointer. + + interpreterProxy failed + ifFalse: [interpreterProxy methodReturnValue: directoryOop] + +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveDirectoryRemoveEmpty [ + + + + | pathPointer pathIndex result pathSize | + pathPointer := interpreterProxy stackValue: 0. + (interpreterProxy isBytes: pathPointer) + ifFalse: [^ interpreterProxy primitiveFail]. + pathIndex := interpreterProxy firstIndexableField: pathPointer. + pathSize := interpreterProxy byteSizeOf: pathPointer. + + result := self cCode: 'NewDirectory_removeEmpty(pathIndex, pathSize)'. + + interpreterProxy failed + ifFalse: [interpreterProxy methodReturnBool: result] + +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveDirectoryRewind [ + + + + | directoryOop directoryPointer | + directoryOop := interpreterProxy stackValue: 0. + interpreterProxy failed ifFalse: [ + directoryPointer := self pointerAtPointer: (interpreterProxy firstIndexableField: directoryOop). + self cCode: 'NewDirectory_rewind(directoryPointer)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy pop: 1 "pop directory; leave rcvr on stack" ]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileClose [ + + + + | fileOop filePointer | + fileOop := interpreterProxy stackValue: 0. + interpreterProxy failed ifFalse: [ + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + self cCode: 'NewFile_close(filePointer)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy pop: 1 "pop file; leave rcvr on stack" ]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileGetSize [ + + + + + | fileOop filePointer fileSize | + fileOop := interpreterProxy stackValue: 0. + interpreterProxy failed ifFalse: [ + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + fileSize := self cCode: 'NewFile_getSize(filePointer)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: (interpreterProxy signed64BitIntegerFor: fileSize)]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileOpen [ + + + +"primitiveOpen: path mode: mode creationDisposition: creationDisposition flags: flags + " + | pathPointer mode creationDisposition flags pathIndex filePointer fileOop pathSize | + flags := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 0). + creationDisposition := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 1). + mode := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 2). + + pathPointer := interpreterProxy stackValue: 3. + (interpreterProxy isBytes: pathPointer) + ifFalse: [^ interpreterProxy primitiveFail]. + pathIndex := interpreterProxy firstIndexableField: pathPointer. + pathSize := interpreterProxy byteSizeOf: pathPointer. + + fileOop := interpreterProxy instantiateClass: interpreterProxy classExternalAddress indexableSize: BytesPerWord. + + filePointer := self cCode: 'NewFile_open(pathIndex, pathSize, mode, creationDisposition, flags)'. + self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop) put: filePointer. + + interpreterProxy failed + ifFalse: [interpreterProxy methodReturnValue: fileOop] + +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileReadInto [ + + + + + + | fileOop filePointer readSize bufferOffset bufferOop bufferPointer readResult | + readSize := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 0). + bufferOffset := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 1). + bufferOop := interpreterProxy stackValue: 2. + fileOop := interpreterProxy stackValue: 3. + + interpreterProxy failed ifFalse: [ + bufferPointer := interpreterProxy firstIndexableField: bufferOop. + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + readResult := self cCode: 'NewFile_read(filePointer, bufferPointer, bufferOffset, readSize)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: (interpreterProxy signed64BitIntegerFor: readResult)]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileReadIntoAtFileOffset [ + + + + + + | fileOop filePointer fileOffset readSize bufferOffset bufferOop bufferPointer readResult | + fileOffset := interpreterProxy positive64BitValueOf: (interpreterProxy stackValue: 0). + readSize := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 1). + bufferOffset := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 2). + bufferOop := interpreterProxy stackValue: 3. + fileOop := interpreterProxy stackValue: 4. + + interpreterProxy failed ifFalse: [ + bufferPointer := interpreterProxy firstIndexableField: bufferOop. + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + readResult := self cCode: 'NewFile_readAtOffset(filePointer, bufferPointer, bufferOffset, readSize, fileOffset)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: (interpreterProxy signed64BitIntegerFor: readResult)]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileSeek [ + + + + | fileOop filePointer seekMode seekOffset | + seekMode := interpreterProxy positive32BitValueOf: (interpreterProxy stackValue: 0). + seekOffset := interpreterProxy signed64BitValueOf: (interpreterProxy stackValue: 1). + fileOop := interpreterProxy stackValue: 2. + interpreterProxy failed ifFalse: [ + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + self cCode: 'NewFile_seek(filePointer, seekOffset, seekMode)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy pop: 3 "pop seek mode, offset and file; leave rcvr on stack"]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileTell [ + + + + + | fileOop filePointer fileSize | + fileOop := interpreterProxy stackValue: 0. + interpreterProxy failed ifFalse: [ + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + fileSize := self cCode: 'NewFile_tell(filePointer)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: (interpreterProxy signed64BitIntegerFor: fileSize)]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileTruncate [ + + + + + | fileOop filePointer newFileSize result | + newFileSize := interpreterProxy positive64BitValueOf: (interpreterProxy stackValue: 0). + fileOop := interpreterProxy stackValue: 1. + interpreterProxy failed ifFalse: [ + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + result := self cCode: 'NewFile_truncate(filePointer, newFileSize)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnBool: result ]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileWriteFrom [ + + + + + + | fileOop filePointer writeSize bufferOffset bufferOop bufferPointer writeResult | + writeSize := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 0). + bufferOffset := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 1). + bufferOop := interpreterProxy stackValue: 2. + fileOop := interpreterProxy stackValue: 3. + + interpreterProxy failed ifFalse: [ + bufferPointer := interpreterProxy firstIndexableField: bufferOop. + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + writeResult := self cCode: 'NewFile_write(filePointer, bufferPointer, bufferOffset, writeSize)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: (interpreterProxy signed64BitIntegerFor: writeResult)]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveFileWriteFromAtFileOffset [ + + + + + + | fileOop filePointer fileOffset writeSize bufferOffset bufferOop bufferPointer writeResult | + fileOffset := interpreterProxy positive64BitValueOf: (interpreterProxy stackValue: 0). + writeSize := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 1). + bufferOffset := interpreterProxy positiveMachineIntegerValueOf: (interpreterProxy stackValue: 2). + bufferOop := interpreterProxy stackValue: 3. + fileOop := interpreterProxy stackValue: 4. + + interpreterProxy failed ifFalse: [ + bufferPointer := interpreterProxy firstIndexableField: bufferOop. + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + writeResult := self cCode: 'NewFile_writeAtOffset(filePointer, bufferPointer, bufferOffset, writeSize, fileOffset)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: (interpreterProxy signed64BitIntegerFor: writeResult)]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveIsAvailable [ + + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: (interpreterProxy trueObject)]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveMemoryMapWithProtection [ + + + + + | fileOop filePointer protectionFlags memoryMappedPointer memoryMappedPointerOop | + protectionFlags := interpreterProxy positive32BitValueOf:(interpreterProxy stackValue: 0). + fileOop := interpreterProxy stackValue: 1. + memoryMappedPointerOop := interpreterProxy instantiateClass: interpreterProxy classExternalAddress indexableSize: BytesPerWord. + interpreterProxy failed ifFalse: [ + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + memoryMappedPointer := self cCode: 'NewFile_memoryMap(filePointer, protectionFlags)'. + self pointerAtPointer: (interpreterProxy firstIndexableField: memoryMappedPointerOop) put: memoryMappedPointer. + ]. + interpreterProxy failed ifFalse: [ interpreterProxy methodReturnValue: memoryMappedPointerOop ]. +] + +{ #category : 'file primitives' } +NewFilePlugin >> primitiveMemoryUnmap [ + + + + | fileOop filePointer | + fileOop := interpreterProxy stackValue: 0. + interpreterProxy failed ifFalse: [ + filePointer := self pointerAtPointer: (interpreterProxy firstIndexableField: fileOop). + self cCode: 'NewFile_memoryUnmap(filePointer)' + ]. + interpreterProxy failed ifFalse: [ interpreterProxy pop: 1 "pop file; leave rcvr on stack" ]. +] diff --git a/smalltalksrc/VMMaker/PharoVMMaker.class.st b/smalltalksrc/VMMaker/PharoVMMaker.class.st index 4ff021cb16..cce6224a5c 100644 --- a/smalltalksrc/VMMaker/PharoVMMaker.class.st +++ b/smalltalksrc/VMMaker/PharoVMMaker.class.st @@ -144,7 +144,7 @@ PharoVMMaker >> generate: interpreterClass memoryManager: memoryManager compiler vmmaker stopOnErrors: stopOnErrors; - internal: #() external: #(FilePlugin SurfacePlugin FloatArrayPlugin); + internal: #() external: #(NewFilePlugin FilePlugin SurfacePlugin FloatArrayPlugin); generateInterpreterFile; generateCogitFiles. generatePlugins ifTrue: [ vmmaker generateExternalPlugins ]] valueSupplyingAnswer:true.]