Skip to content
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
54 changes: 52 additions & 2 deletions Modelica/Resources/C-Sources/ModelicaInternal.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ void ModelicaInternal_setenv(_In_z_ const char* name,
#include <direct.h>
#include <sys/types.h>
#include <sys/stat.h>
#if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

#if defined(__MINGW32__) || defined(__CYGWIN__) /* MinGW and Cygwin have dirent.h */
#include <dirent.h>
Expand Down Expand Up @@ -754,16 +758,62 @@ _Ret_z_ const char* ModelicaInternal_fullPathName(_In_z_ const char* name) {
}

_Ret_z_ const char* ModelicaInternal_temporaryFileName(void) {
/* Get full path name of a temporary file name which does not exist */
/* Atomically create an empty temporary file and return its full path name */
char* fullName;

#if defined(_WIN32)
char tempPath[BUFFER_LENGTH];
char tempFile[BUFFER_LENGTH];
DWORD pathLen = GetTempPathA((DWORD)sizeof(tempPath), tempPath);
if (pathLen == 0 || pathLen >= (DWORD)sizeof(tempPath)) {
ModelicaFormatError("Not possible to get path of temporary directory\nError code: %lu",
(unsigned long)GetLastError());
return "";
}
if (0 == GetTempFileNameA(tempPath, "tmp", 0, tempFile)) {
ModelicaFormatError("Not possible to get temporary filename\nError code: %lu",
(unsigned long)GetLastError());
return "";
}
/* GetTempFileNameA already atomically created an empty file with this unique name */
fullName = ModelicaDuplicateString(tempFile);
ModelicaConvertToUnixDirectorySeparator(fullName);
#elif defined(_POSIX_) || defined(__GNUC__)
/* Use mkstemp to atomically create the temporary file itself (avoiding
the race condition of tmpnam) */
char fileTemplate[BUFFER_LENGTH];
static const char fileSuffix[] = "/modelicaXXXXXX";
const char* tmpDir = getenv("TMPDIR");
int fileDescriptor;
if (tmpDir == NULL || tmpDir[0] == '\0') {
#if defined(P_tmpdir)
tmpDir = P_tmpdir;
#else
tmpDir = "/tmp";
#endif
}
if (strlen(tmpDir) + sizeof(fileSuffix) > sizeof(fileTemplate)) {
ModelicaFormatError("Path of temporary directory is too long\n\"%s\"", tmpDir);
return "";
}
strcpy(fileTemplate, tmpDir);
strcat(fileTemplate, fileSuffix);
fileDescriptor = mkstemp(fileTemplate);
if (fileDescriptor == -1) {
ModelicaFormatError("Not possible to create temporary file\n%s", strerror(errno));
return "";
}
close(fileDescriptor);
fullName = ModelicaDuplicateString(fileTemplate);
ModelicaConvertToUnixDirectorySeparator(fullName);
#else
char* tempName = tmpnam(NULL);
if (tempName == NULL) {
ModelicaFormatError("Not possible to get temporary filename\n%s", strerror(errno));
return "";
}
fullName = ModelicaDuplicateString(tempName);
ModelicaConvertToUnixDirectorySeparator(fullName);
#endif

return fullName;
}
Expand Down
21 changes: 8 additions & 13 deletions Modelica/Utilities/Files.mo
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ Function <strong>splitPathName</strong>(..) splits a path name into its parts.
end splitPathName;

impure function temporaryFileName
"Return arbitrary name of a file that does not exist and is in a directory where access rights allow to write to this file (useful for temporary output of files)"
"Return arbitrary name of an empty file freshly created in a directory where access rights allow to write to this file (useful for temporary output of files)"
extends Modelica.Icons.Function;
output String fileName "Full path name of temporary file";
external "C" fileName=ModelicaInternal_temporaryFileName() annotation(IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaInternal.h\"", Library="ModelicaExternalC");
Expand All @@ -666,24 +666,19 @@ fileName = Files.<strong>temporaryFileName</strong>();
</pre></blockquote>
<h4>Description</h4>
<p>
Return arbitrary name of a file that does not exist
and is in a directory where access rights allow to
write to this file (useful for temporary output of files).
Atomically create an empty file with an arbitrary name in a directory
where access rights allow to write to this file (useful for temporary
output of files), and return its full path name.
</p>
<p>
The created temporary file is not automatically deleted when closed, but needs to be explicitly deleted, e.g. by <strong><a href=\"modelica://Modelica.Utilities.Files.removeFile\">removeFile</a></strong>(fileName).
</p>
<p>
<strong>Warning:</strong>
The underlying C implementation of <strong>ModelicaInternal_temporaryFileName</strong> calls the standard C function <strong>tmpnam</strong>, which has a race condition security problem in the case another process creates a file with the same fileName just after <strong>tmpnam</strong> generated the full path name.
</p>
<h4>Example</h4>
<blockquote><pre>
fileName = Files.temporaryFileName();
-> fileName is the absolute path name of the temporary file
-> fileName is the absolute path name of the (empty) temporary file
Streams.print(String(System.getPid()), fileName);
-> Create the temporary file
Warning: Possible race condition on file access
-> Write to the temporary file
Files.removeFile(fileName);
-> Explicitly delete the temporary file (after use)
</pre></blockquote>
Expand Down Expand Up @@ -782,8 +777,8 @@ In the table below an example call to every function is given:
<td> Split path name in directory, file name kernel, file name extension.</td>
</tr>
<tr><td>fileName = <a href=\"modelica://Modelica.Utilities.Files.temporaryFileName\">temporaryFileName</a>()</td>
<td> Return arbitrary name of a file that does not exist<br>
and is in a directory where access rights allow to<br>
<td> Return arbitrary name of an empty file freshly created<br>
in a directory where access rights allow to<br>
write to this file (useful for temporary output of files).</td>
</tr>
<tr><td>fileReference = <a href=\"modelica://Modelica.Utilities.Files.loadResource\">loadResource</a>(uri)</td>
Expand Down