[sys] Add a function to safely create a unique file

QF currently uses unique file names for screenshots and server-side
demos (and remote snapshots), but they're generally useful.
QFS_NextFilename has been filling this role, but it is highly insecure
in its current implementation. This is the first step to taking care of
that.
This commit is contained in:
Bill Currie 2022-03-31 16:31:13 +09:00
parent 14ad364e17
commit a35bfef24c
2 changed files with 60 additions and 0 deletions

View file

@ -38,6 +38,8 @@
#include <stdint.h>
#include <stdarg.h>
struct dstring_s;
extern struct cvar_s *sys_nostdout;
extern struct cvar_s *sys_extrasleep;
extern struct cvar_s *sys_dead_sleep;
@ -166,6 +168,37 @@ int Sys_CreatePath (const char *path);
*/
char *Sys_ExpandSquiggle (const char *path);
/** Open a newly created file with a guaranteed unique name.
Uniqueness is guaranteed by adding a numeric sequence between the \a
prefix and \a suffix, with a minium of \a mindigits numeric characters
(with any required leading 0s to expand the number to \a mindigits).
The created file has read and write permissions as modified by the OS,
and the handle can be bothe written and read.
\param name dstring into which the name will be generated. Any
existing contents will be lost. If an error occurs,
\a name will be set to the error string.
\param prefix This includes the path to the file and any file name
prefix. The numeric sequence will be appended directly
to the prefix with no directory separator.
\param suffix Optional tail to be appended after the numeric sequence,
usually the file extension. A dot is not added
automatically, it is up to the caller to supply one. NULL
and an empty string are equivalent.
\param mindigits The minimum number of digits to include in the
generated file name. The sequence number will be padded
with 0s in order to meet this menimum. Overflow will
simply produce longer numeric sequence sub-strings.
\return File handle to the newly created file, or a negative
value if an error occured (the negative error code).
Suitable for use with read, write, fdopen, Qdopen, etc.
\note It is the caller's responsibility to close the file.
*/
int Sys_UniqueFile (struct dstring_s *name, const char *prefix,
const char *suffix, int mindigits);
///@}
#endif//__QF_sys_h

View file

@ -60,6 +60,7 @@
# include <pwd.h>
#endif
#include <inttypes.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
@ -1155,3 +1156,29 @@ Sys_ExpandSquiggle (const char *path)
home = ".";
return nva ("%s%s", home, path + 1); // skip leading ~
}
VISIBLE int
Sys_UniqueFile (dstring_t *name, const char *prefix, const char *suffix,
int mindigits)
{
const int flags = O_CREAT | O_EXCL | O_RDWR;
const int mode = 0644;
int64_t seq = 0; // it should take a while to run out
if (!suffix) {
suffix = "";
}
while (1) {
dsprintf (name, "%s%0*"PRIi64"%s", prefix, mindigits, seq, suffix);
int fd = open (name->str, flags, mode);
if (fd >= 0) {
return fd;
}
int err = errno;
if (err != EEXIST) {
dsprintf (name, "%s", strerror (err));
return -err;
}
seq++;
}
}