Skip to content

Commit

Permalink
Implement mkdtemp primitive on Windows (#313)
Browse files Browse the repository at this point in the history
* Implement mkdtemp primitive on Windows

* Windows not in doc

* Move libs to fix compile

* Another attempt to fix link issues

* Fix typo

* Skip, not fail the LSP tests on Windows

* Do it the way Florian recommended
  • Loading branch information
Erik Corry authored Jan 18, 2022
1 parent 8265b11 commit adcca9d
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 9 deletions.
3 changes: 3 additions & 0 deletions packages/host/src/directory.toit
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ The system adds random characters to make the name unique and creates a fresh
directory with the new name.
Returns the name of the created directory.
On Windows the prefix "/tmp/" is recognized, and the system's temporary
directory is used, as returned by the Win32 API GetTempPath() call.
# Examples
```
test_dir := mkdtemp "/tmp/test-"
Expand Down
12 changes: 9 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ else()
endif()

if ("${CMAKE_SYSTEM_NAME}" MATCHES "MSYS")
set(TOIT_WINSOCK_LIBS ws2_32)
set(TOIT_WINDOWS_LIBS ws2_32 rpcrt4)
elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
set(TOIT_WINDOWS_LIBS rpcrt4)
else()
set(TOIT_WINSOCK_LIBS )
set(TOIT_WINDOWS_LIBS )
endif()

# Because of the `CACHE INTERNAL ""` at the end of the `set` we can
Expand All @@ -101,14 +103,18 @@ set(TOIT_LINK_LIBS
${TOIT_NETWORK_LIBS}
${TOIT_LINK_GROUP_END_FLAGS}
toit_compiler
${TOIT_WINSOCK_LIBS}
pthread
${CMAKE_DL_LIBS}
${TOIT_LINK_LIBS_LIBGCC}
${TOIT_LINK_SEGFAULT}
CACHE INTERNAL ""
)

target_link_libraries(
toit_vm
${TOIT_WINDOWS_LIBS}
)

target_link_libraries(
toit.run
${TOIT_LINK_LIBS}
Expand Down
8 changes: 5 additions & 3 deletions src/compiler/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,19 @@ add_executable(
target_link_libraries(toit.compile -static-libstdc++)

if ("${CMAKE_SYSTEM_NAME}" MATCHES "MSYS")
set(TOIT_WINSOCK_LIBS ws2_32)
set(TOIT_WINDOWS_LIBS ws2_32 rpcrt4)
elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
set(TOIT_WINDOWS_LIBS rpcrt4)
else()
set(TOIT_WINSOCK_LIBS )
set(TOIT_WINDOWS_LIBS )
endif()

target_link_libraries(
toit.compile
toit_core
toit_compiler
mbedtls
${TOIT_WINSOCK_LIBS}
${TOIT_WINDOWS_LIBS}
pthread
${CMAKE_DL_LIBS}
)
Expand Down
98 changes: 96 additions & 2 deletions src/primitive_file_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <rpc.h> // For rpcdce.h.
#include <rpcdce.h> // For UuidCreate.
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <windows.h>
#include <unistd.h>

namespace toit {
Expand All @@ -54,6 +57,7 @@ class AutoCloser {
int _fd;
};

// For Posix-like calls, including socket calls.
static Object* return_open_error(Process* process, int err) {
if (err == EPERM || err == EACCES || err == EROFS) PERMISSION_DENIED;
if (err == EMFILE || err == ENFILE || err == ENOSPC) QUOTA_EXCEEDED;
Expand All @@ -64,6 +68,44 @@ static Object* return_open_error(Process* process, int err) {
OTHER_ERROR;
}

// For Windows API calls.
static Object* return_windows_error(Process* process) {
DWORD err = GetLastError();
if (err == ERROR_FILE_NOT_FOUND ||
err == ERROR_INVALID_DRIVE ||
err == ERROR_DEV_NOT_EXIST) {
FILE_NOT_FOUND;
}
if (err == ERROR_TOO_MANY_OPEN_FILES ||
err == ERROR_SHARING_BUFFER_EXCEEDED ||
err == ERROR_TOO_MANY_NAMES ||
err == ERROR_NO_PROC_SLOTS ||
err == ERROR_TOO_MANY_SEMAPHORES) {
QUOTA_EXCEEDED;
}
if (err == ERROR_ACCESS_DENIED ||
err == ERROR_WRITE_PROTECT ||
err == ERROR_NETWORK_ACCESS_DENIED) {
PERMISSION_DENIED;
}
if (err == ERROR_INVALID_HANDLE) {
ALREADY_CLOSED;
}
if (err == ERROR_NOT_ENOUGH_MEMORY ||
err == ERROR_OUTOFMEMORY) {
MALLOC_FAILED;
}
if (err == ERROR_BAD_COMMAND ||
err == ERROR_INVALID_PARAMETER) {
INVALID_ARGUMENT;
}
if (err == ERROR_FILE_EXISTS ||
err == ERROR_ALREADY_ASSIGNED) {
ALREADY_EXISTS;
}
OTHER_ERROR;
}

// Coordinate with utils.toit.
static const int FILE_RDONLY = 1;
static const int FILE_WRONLY = 2;
Expand Down Expand Up @@ -257,7 +299,10 @@ PRIMITIVE(unlink) {
}

PRIMITIVE(rmdir) {
UNIMPLEMENTED_PRIMITIVE;
ARGS(cstring, pathname);
int result = rmdir(pathname);
if (result < 0) return return_open_error(process, errno);
return process->program()->null_object();
}

PRIMITIVE(rename) {
Expand All @@ -281,7 +326,56 @@ PRIMITIVE(mkdir) {
}

PRIMITIVE(mkdtemp) {
UNIMPLEMENTED_PRIMITIVE;
ARGS(cstring, prefix);
DWORD ret;

bool in_standard_tmp_dir = false;
if (strncmp(prefix, "/tmp/", 5) == 0) {
in_standard_tmp_dir = true;
prefix += 5;
}

char accumulator = 0;
for (const char* p = prefix; *p; p++) accumulator |= *p;
if (accumulator & 0x80) INVALID_ARGUMENT; // Only supports ASCII prefix.

static const int UUID_TEXT_LENGTH = 32;

char temp_dir_name[MAX_PATH];
temp_dir_name[0] = '\0';

if (in_standard_tmp_dir) {
// Get the location of the Windows temp directory.
ret = GetTempPath(MAX_PATH, temp_dir_name);
if (ret + 2 > MAX_PATH) INVALID_ARGUMENT;
if (ret == 0) return return_windows_error(process);
strncat(temp_dir_name, "\\", strlen(temp_dir_name) - 1);
}

if (strlen(temp_dir_name) + UUID_TEXT_LENGTH + strlen(prefix) + 1 > MAX_PATH) INVALID_ARGUMENT;

UUID uuid;
ret = UuidCreate(&uuid);
if (ret != RPC_S_OK && ret != RPC_S_UUID_LOCAL_ONLY) OTHER_ERROR;

unsigned char* uuid_string;
ret = UuidToString(&uuid, &uuid_string);
strncat(temp_dir_name, prefix, MAX_PATH - strlen(temp_dir_name) - 1);
strncat(temp_dir_name, char_cast(uuid_string), MAX_PATH - strlen(temp_dir_name) - 1);
RpcStringFree(&uuid_string);

uword total_len = strlen(temp_dir_name);

Error* error = null;
Object* result = process->allocate_byte_array(total_len, &error);
if (result == null) return error;

int posix_result = mkdir(temp_dir_name);
if (posix_result < 0) return return_open_error(process, errno);

memcpy(ByteArray::Bytes(ByteArray::cast(result)).address(), temp_dir_name, total_len);

return result;
}

PRIMITIVE(is_open_file) {
Expand Down
5 changes: 4 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,12 @@ foreach(file ${TOIT_SLOW_TESTS})
set_tests_properties(${file} PROPERTIES TIMEOUT ${SLOW_TIMEOUT})
endforeach()

if ((NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") AND (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "MSYS"))
add_subdirectory(lsp)
endif()

add_subdirectory(minus_s)
add_subdirectory(negative)
add_subdirectory(lsp)
add_subdirectory(optimizations)
add_subdirectory(ctest)
add_subdirectory(toitp)
Expand Down
18 changes: 18 additions & 0 deletions tests/mkdtemp_test.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (C) 2020 Toitware ApS. All rights reserved.
import expect show *
import host.directory
import host.file

main:
// Make a temporary directory in the current dir.
tmp_dir := directory.mkdtemp "foo-"
print tmp_dir
expect (file.is_directory tmp_dir)
directory.rmdir tmp_dir

// Make a temporary directory in the system dir.
tmp_dir = directory.mkdtemp "/tmp/foo-"
print tmp_dir
expect (file.is_directory tmp_dir)
directory.rmdir tmp_dir

0 comments on commit adcca9d

Please sign in to comment.