Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create temporary files and folders #10966

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,9 @@ if(ANDROID)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

if(SDL_HAPTIC)
set(SDL_HAPTIC_ANDROID 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/android/*.c")
Expand Down Expand Up @@ -1452,6 +1455,9 @@ elseif(EMSCRIPTEN)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

if(SDL_CAMERA)
set(SDL_CAMERA_DRIVER_EMSCRIPTEN 1)
set(HAVE_CAMERA TRUE)
Expand Down Expand Up @@ -1786,6 +1792,9 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

set(SDL_TIME_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
set(HAVE_SDL_TIME TRUE)
Expand Down Expand Up @@ -1982,6 +1991,7 @@ elseif(WINDOWS)
set(SDL_FILESYSTEM_WINDOWS 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/windows/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
set(HAVE_SDL_TMPFS TRUE) # SDL_tmpfs is included in the windows sources

set(SDL_FSOPS_WINDOWS 1)
set(HAVE_SDL_FSOPS TRUE)
Expand Down Expand Up @@ -2234,6 +2244,9 @@ elseif(APPLE)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

if(SDL_SENSOR)
if(IOS OR VISIONOS OR WATCHOS)
set(SDL_SENSOR_COREMOTION 1)
Expand Down Expand Up @@ -2437,6 +2450,9 @@ elseif(HAIKU)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

set(SDL_TIME_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
set(HAVE_SDL_TIME TRUE)
Expand Down Expand Up @@ -2477,6 +2493,9 @@ elseif(RISCOS)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
set(HAVE_SDL_FSOPS TRUE)

sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_posix_tmpfs.c")
set(HAVE_SDL_TMPFS TRUE)

set(SDL_TIME_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
set(HAVE_SDL_TIME TRUE)
Expand Down Expand Up @@ -2986,6 +3005,10 @@ if(NOT HAVE_SDL_FSOPS)
set(SDL_FSOPS_DUMMY 1)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/SDL_sysfsops.c")
endif()
if (NOT HAVE_SDL_TMPFS)
set(SDL_TMPFS_DUMMY 1)
sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/SDL_dummy_tmpfs.c")
endif()
if(NOT HAVE_SDL_LOCALE)
set(SDL_LOCALE_DUMMY 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/dummy/*.c")
Expand Down
82 changes: 79 additions & 3 deletions include/SDL3/SDL_filesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_iostream.h>

#include <SDL3/SDL_begin_code.h>

Expand Down Expand Up @@ -137,9 +138,10 @@ extern SDL_DECLSPEC char * SDLCALL SDL_GetPrefPath(const char *org, const char *
/**
* The type of the OS-provided default folder for a specific purpose.
*
* Note that the Trash folder isn't included here, because trashing files
* usually involves extra OS-specific functionality to remember the file's
* original location.
* Note that many common folders, like the Trash, the Temp folder or
* app-specific folders like AppData are not listed here; using them properly
* requires more treatment than fetching the folder path and using it. To use
* these folders, see their dedicated functions.
*
* The folders supported per platform are:
*
Expand Down Expand Up @@ -363,6 +365,80 @@ extern SDL_DECLSPEC SDL_bool SDLCALL SDL_GetPathInfo(const char *path, SDL_PathI
*/
extern SDL_DECLSPEC char ** SDLCALL SDL_GlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count);

/**
* Create a secure temporary file.
*
* This function is not path-based to avoid race conditions. Returning a path
* and letting the caller create the file opens a time-of-check-to-time-of-use
* (TOCTOU) safety issue, where an attacker can use the small delay between the
* moment the name is generated and the moment the file is created to create
* the file first and give it undesirable attributes, such as giving itself
* full read/write access to the file, or making the file a symlink to another,
* sensitive file.
*
* \returns an open IOStream object to the file, or NULL on error; call
* SDL_GetError() for details.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_CreateUnsafeTempFile
* \sa SDL_CreateTempFolder
*/
extern SDL_DECLSPEC SDL_IOStream *SDLCALL SDL_CreateSafeTempFile(void);

/**
* Create a temporary file, with less security considerations.
*
* Unlike SDL_CreateSafeTempFile(), this function provides a path, which can
* then be used like any other file on the filesystem. This has security
* implications; an attacker could exploit the small delay between the moment
* the name is generated and the moment the file is created to create the file
* first and give it undesirable attributes, such as giving itself full
* read/write access to the file, or making the file a symlink to another,
* sensitive file.
*
* The path string is owned by the caller and must be freed with SDL_free().
*
* \returns an absolute path to the temporary file, or NULL on error; call
* SDL_GetError() for details. If a path is returned, it is encoded in
* OS-specific format, and is guaranteed to finish with a path
* separator.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_CreateSafeTempFile
* \sa SDL_CreateTempFolder
*/
extern SDL_DECLSPEC char *SDLCALL SDL_CreateUnsafeTempFile(void);

/**
* Create a temporary folder.
*
* Keep in mind any program running as the same user as your program can access
* the contents of the folders and the files in it. Do not perform sensitive
* tasks using the temporary folder. If you need one or more temporary files
* for sensitive purposes, use SDL_CreateSafeTempFile().
*
* The path string is owned by the caller and must be freed with SDL_free().
*
* \returns an absolute path to the temporary folder, or NULL on error; call
* SDL_GetError() for details. If a path is returned, it is encoded in
* OS-specific format, and is guaranteed to finish with a path
* separator.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_CreateSafeTempFile
* \sa SDL_CreateUnsafeTempFile
*/
extern SDL_DECLSPEC char *SDLCALL SDL_CreateTempFolder(void);

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
Expand Down
15 changes: 15 additions & 0 deletions src/filesystem/SDL_filesystem.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,21 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return path;
}

SDL_IOStream *SDL_CreateSafeTempFile(void)
{
return SDL_SYS_CreateSafeTempFile();
}

char *SDL_CreateUnsafeTempFile(void)
{
return SDL_SYS_CreateUnsafeTempFile();
}

char *SDL_CreateTempFolder(void)
{
return SDL_SYS_CreateTempFolder();
}


void SDL_InitFilesystem(void)
{
Expand Down
4 changes: 4 additions & 0 deletions src/filesystem/SDL_sysfilesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ typedef SDL_bool (*SDL_GlobEnumeratorFunc)(const char *path, SDL_EnumerateDirect
typedef SDL_bool (*SDL_GlobGetPathInfoFunc)(const char *path, SDL_PathInfo *info, void *userdata);
extern char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count, SDL_GlobEnumeratorFunc enumerator, SDL_GlobGetPathInfoFunc getpathinfo, void *userdata);

extern SDL_IOStream *SDL_SYS_CreateSafeTempFile(void);
extern char *SDL_SYS_CreateUnsafeTempFile(void);
extern char *SDL_SYS_CreateTempFolder(void);

#endif

41 changes: 41 additions & 0 deletions src/filesystem/dummy/SDL_dummy_tmpfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <[email protected]>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#include "SDL_internal.h"
#include "../SDL_sysfilesystem.h"

SDL_IOStream *SDL_SYS_CreateSafeTempFile(void)
{
SDL_Unsupported();
return NULL;
}

char *SDL_SYS_CreateUnsafeTempFile(void)
{
SDL_Unsupported();
return NULL;
}

char *SDL_SYS_CreateTempFolder(void)
{
SDL_Unsupported();
return NULL;
}
90 changes: 90 additions & 0 deletions src/filesystem/posix/SDL_posix_tmpfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <[email protected]>

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#include "SDL_internal.h"
#include "../SDL_sysfilesystem.h"
#include "../../file/SDL_iostream_c.h"

#include <unistd.h>
#include <string.h>
#include <errno.h>

SDL_IOStream *SDL_SYS_CreateSafeTempFile(void)
{
FILE *file = tmpfile();

if (!file) {
SDL_SetError("Could not tmpfile(): %s", strerror(errno));
return NULL;
}

return SDL_IOFromFP(file, true);
}

char *SDL_SYS_CreateUnsafeTempFile(void)
{
/* TODO: Check for possible alternatives to /tmp, like $TMP */
char template[] = "/tmp/tmp.XXXXXX";

char *file = SDL_strdup(template);

if (!file) {
return NULL;
}

int fd = mkstemp(file);

if (fd < 0) {
SDL_free(file);
SDL_SetError("Could not mkstemp(): %s", strerror(errno));
return NULL;
}

/* Normal usage of mkstemp() would use the file descriptor rather than the
path, to avoid issues. In this function, security is assumed to be
unimportant, so no need to worry about it. */
/* See https://stackoverflow.com/questions/27680807/mkstemp-is-it-safe-to-close-descriptor-and-reopen-it-again */
close(fd);

return file;
}

char *SDL_SYS_CreateTempFolder(void)
{
/* TODO: Check for possible alternatives to /tmp, like $TMP */
char template[] = "/tmp/tmp.XXXXXX";

char *folder = SDL_strdup(template);

if (!folder) {
return NULL;
}

char *res = mkdtemp(folder);

if (!res) {
SDL_free(folder);
SDL_SetError("Could not mkdtemp(): %s", strerror(errno));
return NULL;
}

return folder;
}
2 changes: 2 additions & 0 deletions src/filesystem/unix/SDL_sysfilesystem.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
// System dependent filesystem routines

#include "../SDL_sysfilesystem.h"
#include "../../file/SDL_iostream_c.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
Expand Down
Loading
Loading