From da182f1c2fc79cb47316f3224e48777cb25fbbec Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 5 Nov 2022 18:07:45 +0100 Subject: [PATCH] libc: Add mktemp() and mkdtemp(), along with a test for mktemp --- libs/libc/include/stdlib.h | 5 +++- libs/libc/src/stdlib.cpp | 50 ++++++++++++++++++++++++++++++++++++-- tests/libc/Makefile | 2 +- tests/libc/Test.c | 2 ++ tests/libc/stdlib.c | 26 ++++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) diff --git a/libs/libc/include/stdlib.h b/libs/libc/include/stdlib.h index 397c9d3d..5a67e0c6 100644 --- a/libs/libc/include/stdlib.h +++ b/libs/libc/include/stdlib.h @@ -120,7 +120,10 @@ extern "C" size_t mbstowcs(wchar_t* dest, const char* src, size_t n); // Not implemented. - char* mktemp(char* base); // Not implemented. + /* Generate a unique filename from the template string str. The last 6 bytes of str must be 'XXXXXX', and they will + * be replaced with random characters. This function is deprecated due to race conditions; between the name being + * generated and creating a file with that name, that filename could have already been used. */ + __lc_is_deprecated char* mktemp(char* str); #ifdef __cplusplus } diff --git a/libs/libc/src/stdlib.cpp b/libs/libc/src/stdlib.cpp index d924674e..4a1ac2f5 100644 --- a/libs/libc/src/stdlib.cpp +++ b/libs/libc/src/stdlib.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include #include @@ -188,8 +190,52 @@ extern "C" NOT_IMPLEMENTED("mbstowcs"); } - char* mktemp(char*) + static void gentemp(char* startptr) { - NOT_IMPLEMENTED("mktemp"); + for (int i = 0; i < 6; i++) + { + startptr[i] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"[rand() % 63]; + } + } + + char* mktemp(char* str) + { + size_t len = strlen(str); + if (len < 6) + { + errno = EINVAL; + return NULL; + } + char* startptr = str + (len - 6); + if (strncmp(startptr, "XXXXXX", 6)) + { + errno = EINVAL; + return NULL; + } + do { + gentemp(startptr); + } while (access(str, F_OK) == 0); + return str; + } + + char* mkdtemp(char* str) + { + size_t len = strlen(str); + if (len < 6) + { + errno = EINVAL; + return NULL; + } + char* startptr = str + (len - 6); + if (strncmp(startptr, "XXXXXX", 6)) + { + errno = EINVAL; + return NULL; + } + do { + gentemp(startptr); + } while (mkdir(str, 0700) < 0 && errno == EEXIST); + if (errno) return NULL; + return str; } } \ No newline at end of file diff --git a/tests/libc/Makefile b/tests/libc/Makefile index 1ba9da60..db92eeec 100644 --- a/tests/libc/Makefile +++ b/tests/libc/Makefile @@ -4,7 +4,7 @@ DESTDIR := $(LUNA_ROOT)/initrd/bin build: @mkdir -p $(TESTDIR)/bin $(LUNA_ROOT)/tools/sync-libc.sh - $(CC) $(TESTDIR)/string.c $(TESTDIR)/stdlib.c $(TESTDIR)/Test.c -I$(LUNA_ROOT)/tests -o $(TESTDIR)/bin/test-libc -Wall -Wextra -Wno-stringop-overread -Werror + $(CC) $(TESTDIR)/string.c $(TESTDIR)/stdlib.c $(TESTDIR)/Test.c -I$(LUNA_ROOT)/tests -o $(TESTDIR)/bin/test-libc -Wall -Wextra -Wno-deprecated-declarations -Wno-stringop-overread -Werror install: $(LUNA_ROOT)/tools/clean.sh diff --git a/tests/libc/Test.c b/tests/libc/Test.c index 07d30a79..5e188ef5 100644 --- a/tests/libc/Test.c +++ b/tests/libc/Test.c @@ -24,6 +24,7 @@ DEFINE_TEST(atoll); DEFINE_TEST(srand); DEFINE_TEST(malloc); DEFINE_TEST(calloc); +DEFINE_TEST(mktemp); int main() { @@ -51,4 +52,5 @@ int main() RUN_TEST(srand); RUN_TEST(malloc); RUN_TEST(calloc); + RUN_TEST(mktemp); } \ No newline at end of file diff --git a/tests/libc/stdlib.c b/tests/libc/stdlib.c index db5a56ce..0e754810 100644 --- a/tests/libc/stdlib.c +++ b/tests/libc/stdlib.c @@ -1,5 +1,7 @@ #include "Test.h" #include +#include +#include DEFINE_TEST(atoi) { @@ -136,5 +138,29 @@ DEFINE_TEST(calloc) free(ptr); + TEST_SUCCESS(); +} + +DEFINE_TEST(mktemp) +{ + START_TEST(mktemp); + + char template[] = "/tmp/file.XXXXXX"; + + const char* template2 = "/tmp/file.XXXXXX"; + + char* ptr = mktemp(template); + + EXPECT_NOT_EQ(ptr, NULL); // mktemp only fails if we give it an invalid template. + + int rc = access(ptr, F_OK); + + EXPECT_NOT_EQ(rc, 0); // FIXME: This could actually happen, since that's why mktemp is deprecated. + // Another process could create the file between generating the name and actually using it. + + rc = strcmp(ptr, template2); + + EXPECT_NOT_EQ(rc, 0); + TEST_SUCCESS(); } \ No newline at end of file