Skip to content

Commit

Permalink
Add temp files and folders
Browse files Browse the repository at this point in the history
  • Loading branch information
Semphris committed Sep 27, 2024
1 parent 03ae792 commit a876343
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 3 deletions.
84 changes: 81 additions & 3 deletions include/SDL3/SDL_filesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,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 +364,83 @@ 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);

/* Carried over from SDL_iostream.h */
typedef struct SDL_IOStream SDL_IOStream;

/**
* 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

64 changes: 64 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 Expand Up @@ -615,4 +617,66 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
return result;
}

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;
}

#endif // SDL_FILESYSTEM_UNIX

0 comments on commit a876343

Please sign in to comment.