#define STRGEN_H
#include <stdbool.h>
+#include <sys/types.h>
char *random_str(void);
char *random_plus_addr(const char *addr);
+/*
+ * Atomically create a uniquely-named file under dirfd. Its name is
+ * "<prefix><random>" (prefix may be NULL or empty); the file is opened
+ * O_RDWR|O_CREAT|O_EXCL and the call retries on EEXIST. On success
+ * returns the open fd and, if name_out != NULL, a malloc'd copy of the
+ * basename that was created (caller frees). On other errors returns -1
+ * with errno set and sets *name_out to NULL. Use AT_FDCWD with an
+ * absolute or list-relative prefix to create via an absolute path.
+ */
+int mkuniqfileat(int dirfd, const char *prefix, mode_t mode,
+ char **name_out);
+char *random_plus_addr(const char *addr);
char *genlistname(const char *listaddr);
const char *genlistfqdn(const char *listaddr);
char *hostnamestr(void);
if (incfd == -1)
err(EXIT_FAILURE, "Cannot open(%s/incoming)", listdir);
- do {
- free(randomstr);
- randomstr = random_str();
- fd = openat(incfd, randomstr, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
- } while(fd < 0 && errno == EEXIST);
-
- xasprintf(&infilename, "%s/incoming/%s", listdir, randomstr);
- free(randomstr);
-
- if(fd < 0) {
+ fd = mkuniqfileat(incfd, "", S_IRUSR|S_IWUSR, &randomstr);
+ if (fd < 0) {
log_error(LOG_ARGS, "could not create mail file in "
"%s/incoming directory", listdir);
- free(infilename);
+ free(randomstr);
exit(EXIT_FAILURE);
}
+ xasprintf(&infilename, "%s/incoming/%s", listdir, randomstr);
+ free(randomstr);
+
if(process_mail(STDIN_FILENO, fd, listfd) != 0) {
log_error(LOG_ARGS, "Could not receive mail");
exit(EXIT_FAILURE);
{
int outfd;
FILE *outf;
- char *tmp, *retstr = NULL;
+ char *qprefix, *retstr = NULL;
-
- do {
- tmp = random_str();
- if (retstr)
- free(retstr);
- xasprintf(&retstr, "%s/queue/%s", ml->dir, tmp);
- free(tmp);
-
- outfd = open(retstr, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
-
- } while ((outfd < 0) && (errno == EEXIST));
+ xasprintf(&qprefix, "%s/queue/", ml->dir);
+ outfd = mkuniqfileat(AT_FDCWD, qprefix, S_IRUSR|S_IWUSR, &retstr);
+ free(qprefix);
if(outfd < 0) {
- log_error(LOG_ARGS, "Could not open std mail %s", retstr);
+ log_error(LOG_ARGS, "Could not open std mail in %s/queue",
+ ml->dir);
free(retstr);
close_text(txt);
return NULL;
size_t len;
text *txt = NULL;
char buf[100];
- char *tmp, *queuename = NULL, *subject = NULL, *line = NULL;
+ char *tmp, *queuename = NULL, *subject = NULL, *line = NULL, *qprefix;
char *boundary = NULL;
thread_list_state *tls;
FILE *f = NULL;
if (firstindex > lastindex)
return -1;
- do {
- tmp = random_str();
- free(queuename);
- xasprintf(&queuename, "%s/queue/%s", ml->dir, tmp);
- free(tmp);
- fd = open(queuename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
- } while ((fd < 0) && (errno == EEXIST));
-
+ xasprintf(&qprefix, "%s/queue/", ml->dir);
+ fd = mkuniqfileat(AT_FDCWD, qprefix, S_IRUSR|S_IWUSR, &queuename);
+ free(qprefix);
if (fd < 0) {
- log_error(LOG_ARGS, "Could not open digest queue file '%s'",
- queuename);
+ log_error(LOG_ARGS, "Could not open digest queue file in "
+ "%s/queue", ml->dir);
free(queuename);
return -1;
}
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#include <netdb.h>
#include <libgen.h>
#include <time.h>
return dest;
}
+int
+mkuniqfileat(int dirfd, const char *prefix, mode_t mode, char **name_out)
+{
+ char *name = NULL;
+ int fd;
+
+ if (prefix == NULL)
+ prefix = "";
+ for (;;) {
+ char *r = random_str();
+
+ xasprintf(&name, "%s%s", prefix, r);
+ free(r);
+ fd = openat(dirfd, name, O_RDWR|O_CREAT|O_EXCL, mode);
+ if (fd >= 0)
+ break;
+ free(name);
+ name = NULL;
+ if (errno != EEXIST) {
+ if (name_out != NULL)
+ *name_out = NULL;
+ return (-1);
+ }
+ }
+ if (name_out != NULL)
+ *name_out = name;
+ else
+ free(name);
+ return (fd);
+}
+
char *genlistname(const char *listaddr)
{
const char *atsign;
exit(EXIT_FAILURE);
}
- do {
- free(randomstr);
- randomstr = random_str();
- subconffd = openat(fd, randomstr, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
- } while ((subconffd < 0) && (errno == EEXIST));
-
- if(subconffd < 0) {
- log_error(LOG_ARGS, "Could not open '%s/%s/%s'", ml->dir,
- sub ? "subconf" : "unsubconf", randomstr);
+ subconffd = mkuniqfileat(fd, "", S_IRUSR|S_IWUSR, &randomstr);
+ if (subconffd < 0) {
+ log_error(LOG_ARGS, "Could not open '%s/%s' unique file",
+ ml->dir, sub ? "subconf" : "unsubconf");
free(randomstr);
exit(EXIT_FAILURE);
}
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
+#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>
ATF_TC_WITHOUT_HEAD(find_addr_line_edge);
ATF_TC_WITHOUT_HEAD(is_subbed_crlf_prefix);
ATF_TC_WITHOUT_HEAD(unsubscribe_crlf_prefix);
+ATF_TC_WITHOUT_HEAD(mkuniqfileat_test);
ATF_TC_WITHOUT_HEAD(getaddrsfromfile);
ATF_TC_WITHOUT_HEAD(dumpfd2fd);
ATF_TC_WITHOUT_HEAD(dumpfd2fd_large);
}
}
+ATF_TC_BODY(mkuniqfileat_test, tc)
+{
+ int dfd, fd, fd2;
+ char *name = NULL, *name2 = NULL, *name3 = NULL, *name4 = NULL;
+
+ ATF_REQUIRE(mkdir("mkuniq", 0755) == 0);
+ dfd = open("mkuniq", O_DIRECTORY);
+ ATF_REQUIRE(dfd >= 0);
+
+ /* Bare unique name, returned via name_out. */
+ fd = mkuniqfileat(dfd, "", S_IRUSR|S_IWUSR, &name);
+ ATF_REQUIRE_MSG(fd >= 0, "mkuniqfileat failed: %s", strerror(errno));
+ ATF_REQUIRE(name != NULL);
+ ATF_REQUIRE_MSG(faccessat(dfd, name, F_OK, 0) == 0,
+ "created file not visible");
+ ATF_REQUIRE(dprintf(fd, "x") == 1);
+ close(fd);
+
+ /* A second call must yield a different name. */
+ fd2 = mkuniqfileat(dfd, "", S_IRUSR|S_IWUSR, &name2);
+ ATF_REQUIRE(fd2 >= 0);
+ ATF_REQUIRE(name2 != NULL);
+ ATF_REQUIRE_MSG(strcmp(name, name2) != 0,
+ "two calls produced the same name %s", name);
+ close(fd2);
+
+ /* Prefixed name. */
+ fd = mkuniqfileat(dfd, "pre", S_IRUSR|S_IWUSR, &name3);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE_MSG(strncmp(name3, "pre", 3) == 0,
+ "prefix not honoured: %s", name3);
+ ATF_REQUIRE_MSG(faccessat(dfd, name3, F_OK, 0) == 0,
+ "prefixed file not visible");
+ close(fd);
+
+ /* name_out == NULL is allowed. */
+ fd = mkuniqfileat(dfd, "", S_IRUSR|S_IWUSR, NULL);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ /* AT_FDCWD with a relative path prefix (absolute-path usage). */
+ fd = mkuniqfileat(AT_FDCWD, "mkuniq/", S_IRUSR|S_IWUSR, &name4);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE_MSG(strncmp(name4, "mkuniq/", 7) == 0,
+ "cwd-prefix not honoured: %s", name4);
+ ATF_REQUIRE_MSG(access(name4, F_OK) == 0, "cwd-prefix file not visible");
+ close(fd);
+
+ /* Bad dirfd: returns -1, does not exit, sets *name_out to NULL. */
+ fd = mkuniqfileat(-1, "", S_IRUSR|S_IWUSR, &name);
+ ATF_REQUIRE_EQ_MSG(fd, -1, "expected failure on bad dirfd");
+ ATF_REQUIRE(name == NULL);
+
+ free(name);
+ free(name2);
+ free(name3);
+ free(name4);
+ close(dfd);
+}
+
ATF_TC_BODY(getaddrsfromfile, tc)
{
strlist stl = vec_init();
ATF_TP_ADD_TC(tp, find_addr_line_edge);
ATF_TP_ADD_TC(tp, is_subbed_crlf_prefix);
ATF_TP_ADD_TC(tp, unsubscribe_crlf_prefix);
+ ATF_TP_ADD_TC(tp, mkuniqfileat_test);
ATF_TP_ADD_TC(tp, getaddrsfromfile);
ATF_TP_ADD_TC(tp, dumpfd2fd);
ATF_TP_ADD_TC(tp, dumpfd2fd_large);