#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#define MAX_TEMPLATE_LEN 10
#define MAX_TRIES 100
#define MIN_Xs 6
#define SUFFIX ".suff"
#define SLEN (sizeof SUFFIX - 1)
long pg;
int
check(int fd, char const *path, char const *prefix, size_t plen,
char const *suffix, size_t slen, int tlen)
{
struct stat sb, fsb;
char const *p;
if (tlen < MIN_Xs) {
if (fd >= 0)
errx(1, "mkstemp(%s) succeed with too few Xs", path);
if (errno != EINVAL)
err(1, "mkstemp(%s) failed with wrong errno", path);
return 1;
}
if (fd < 0)
err(1, "mkstemp(%s)", path);
if (stat(path, &sb))
err(1, "stat(%s)", path);
if (fstat(fd, &fsb))
err(1, "fstat(%d==%s)", fd, path);
if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino)
errx(1, "stat mismatch");
close(fd);
if (memcmp(path, prefix, plen) != 0)
errx(1, "prefix changed! %s vs %s", prefix, path);
if (memcmp(path + plen + tlen, suffix, slen + 1) != 0)
errx(1, "suffix changed! %s vs %s", suffix, path);
for (p = path + plen; p < path + plen + tlen; p++)
if (*p == '\0')
errx(1, "unexpected truncation");
else if (*p == 'X')
return 0;
return 1;
}
void
try_mkstemp(char *p, char const *prefix, int len)
{
char *q;
size_t plen = strlen(prefix);
int tries, fd;
for (tries = 0; tries < MAX_TRIES; tries++) {
memcpy(p, prefix, plen);
memset(p + plen, 'X', len);
p[plen + len] = '\0';
fd = mkstemp(p);
if (check(fd, p, prefix, plen, "", 0, len))
return;
}
errx(1, "exceeded MAX_TRIES");
}
void
try_mkstemps(char *p, char const *prefix, int len, char const *suffix)
{
char *q;
size_t plen = strlen(prefix);
size_t slen = strlen(suffix);
int tries, fd;
for (tries = 0; tries < MAX_TRIES; tries++) {
memcpy(p, prefix, plen);
memset(p + plen, 'X', len);
memcpy(p + plen + len, suffix, slen + 1);
fd = mkstemps(p, slen);
if (check(fd, p, prefix, plen, suffix, slen, len))
return;
}
errx(1, "exceeded MAX_TRIES");
}
int
main(void)
{
struct stat sb, fsb;
char cwd[PATH_MAX + 1];
char *p;
size_t clen;
int i;
pg = sysconf(_SC_PAGESIZE);
if (getcwd(cwd, sizeof cwd - 1) == NULL)
err(1, "getcwd");
clen = strlen(cwd);
cwd[clen++] = '/';
cwd[clen] = '\0';
p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
if (p == MAP_FAILED)
err(1, "mmap");
if (mprotect(p, pg, PROT_NONE) || mprotect(p + pg * 2, pg, PROT_NONE))
err(1, "mprotect");
p += pg;
i = MAX_TEMPLATE_LEN + 1;
while (i-- > 0) {
try_mkstemp(p, "", i);
try_mkstemp(p + pg - i - 1, "", i);
try_mkstemp(p, cwd, i);
try_mkstemp(p + pg - clen - i - 1, cwd, i);
try_mkstemps(p, "", i, "");
try_mkstemps(p + pg - i - 1, "", i, "");
try_mkstemps(p, cwd, i, "");
try_mkstemps(p + pg - clen - i - 1, cwd, i, "");
try_mkstemps(p, "", i, SUFFIX);
try_mkstemps(p + pg - i - SLEN - 1, "", i, SUFFIX);
try_mkstemps(p, cwd, i, SUFFIX);
try_mkstemps(p + pg - clen - i - SLEN - 1, cwd, i, SUFFIX);
}
return 0;
}