commit 286e3af1c71b91c676fdece9f435798f08ffb26b
parent 3b037c41f6b6551533cabed8a8e691c7f5cac494
Author: Santtu Lakkala <inz@inz.fi>
Date: Wed, 9 Mar 2022 15:43:02 +0200
Use dirfds for tree creation
Instead of having the full paths of files, keep per-channel directories
open as file descriptors and open the "out" file with openat(). This
reduces the need for custom string mangling (for make-all-directories)
and somewhat reduces the amount of strings in memory (but adds to number
of open file descriptors).
Diffstat:
M | ii.c | | | 100 | ++++++++++++++++++++++++++++++++++++------------------------------------------- |
1 file changed, 45 insertions(+), 55 deletions(-)
diff --git a/ii.c b/ii.c
@@ -37,10 +37,9 @@ enum { TOK_NICKSRV = 0, TOK_USER, TOK_CMD, TOK_CHAN, TOK_ARG, TOK_TEXT, TOK_LAST
typedef struct Channel Channel;
struct Channel {
Channel *next;
+ int dfd;
int fdin;
char name[IRC_CHANNEL_MAX]; /* channel name (normalized) */
- char inpath[PATH_MAX]; /* input path */
- char outpath[PATH_MAX]; /* output path */
};
static Channel * channel_add(const char *);
@@ -54,8 +53,7 @@ static int channel_open(Channel *);
static void channel_print(Channel *, const char *);
static int channel_reopen(Channel *);
static void channel_rm(Channel *);
-static void create_dirtree(const char *);
-static void create_filepath(char *, size_t, const char *, const char *, const char *);
+static int ensure_dirtree(const char *);
static void ewritestr(int, const char *);
static void handle_channels_input(int, Channel *);
static void handle_server_output(int);
@@ -81,6 +79,7 @@ static Channel *channelmaster = NULL;
static char nick[32]; /* active nickname at runtime */
static char _nick[32]; /* nickname at startup */
static char ircpath[PATH_MAX]; /* irc dir (-i) */
+static int dirfd;
static char msg[IRC_MSG_MAX]; /* message buf used for communication */
static void
@@ -110,29 +109,33 @@ ewritestr(int fd, const char *s)
}
/* creates directories bottom-up, if necessary */
-static void
-create_dirtree(const char *dir)
+static int
+ensure_dirtree(const char *dir)
{
char tmp[PATH_MAX], *p;
+ char *part;
struct stat st;
size_t len;
+ int dfd;
+
+ dfd = open(*dir == '/' ? "/" : ".", O_RDONLY | O_DIRECTORY);
+ if (dfd < 0)
+ return -1;
strlcpy(tmp, dir, sizeof(tmp));
- len = strlen(tmp);
- if (len > 0 && tmp[len - 1] == '/')
- tmp[len - 1] = '\0';
+ for (part = strtok(tmp, "/"); part; part = strtok(NULL, "/")) {
+ int sdfd;
- if ((stat(tmp, &st) != -1) && S_ISDIR(st.st_mode))
- return; /* dir exists */
+ (void)mkdirat(dfd, part, S_IRWXU);
+ sdfd = openat(dfd, part, O_RDONLY | O_DIRECTORY);
+ close(dfd);
- for (p = tmp + 1; *p; p++) {
- if (*p != '/')
- continue;
- *p = '\0';
- mkdir(tmp, S_IRWXU);
- *p = '/';
+ if (sdfd < 0)
+ return -1;
+ dfd = sdfd;
}
- mkdir(tmp, S_IRWXU);
+
+ return dfd;
}
static void
@@ -162,32 +165,6 @@ channel_normalize_name(char *s)
*p = '\0';
}
-static void
-create_filepath(char *filepath, size_t len, const char *path,
- const char *channel, const char *suffix)
-{
- int r;
-
- if (channel[0]) {
- r = snprintf(filepath, len, "%s/%s", path, channel);
- if (r < 0 || (size_t)r >= len)
- goto error;
- create_dirtree(filepath);
- r = snprintf(filepath, len, "%s/%s/%s", path, channel, suffix);
- if (r < 0 || (size_t)r >= len)
- goto error;
- } else {
- r = snprintf(filepath, len, "%s/%s", path, suffix);
- if (r < 0 || (size_t)r >= len)
- goto error;
- }
- return;
-
-error:
- fprintf(stderr, "%s: path to irc directory too long\n", argv0);
- exit(1);
-}
-
static int
channel_open(Channel *c)
{
@@ -195,14 +172,14 @@ channel_open(Channel *c)
struct stat st;
/* make "in" fifo if it doesn't exist already. */
- if (lstat(c->inpath, &st) != -1) {
+ if (fstatat(c->dfd, "in", &st, 0) != -1) {
if (!(st.st_mode & S_IFIFO))
return -1;
- } else if (mkfifo(c->inpath, S_IRWXU)) {
+ } else if (mkfifoat(c->dfd, "in", S_IRWXU)) {
return -1;
}
c->fdin = -1;
- fd = open(c->inpath, O_RDONLY | O_NONBLOCK, 0);
+ fd = openat(c->dfd, "in", O_RDONLY | O_NONBLOCK, 0);
if (fd == -1)
return -1;
c->fdin = fd;
@@ -237,10 +214,13 @@ channel_new(const char *name)
strlcpy(c->name, name, sizeof(c->name));
channel_normalize_name(c->name);
- create_filepath(c->inpath, sizeof(c->inpath), ircpath,
- channelpath, "in");
- create_filepath(c->outpath, sizeof(c->outpath), ircpath,
- channelpath, "out");
+ if (*channelpath) {
+ mkdirat(dirfd, channelpath, S_IRWXU);
+ c->dfd = openat(dirfd, channelpath, O_RDONLY | O_DIRECTORY);
+ } else {
+ c->dfd = dup(dirfd);
+ }
+
return c;
}
@@ -304,7 +284,8 @@ channel_leave(Channel *c)
c->fdin = -1;
}
/* remove "in" file on leaving the channel */
- unlink(c->inpath);
+ unlinkat(c->dfd, "in", 0);
+ close(c->dfd);
channel_rm(c);
}
@@ -425,10 +406,12 @@ tokenize(char **result, size_t reslen, char *str, int delim)
static void
channel_print(Channel *c, const char *buf)
{
- FILE *fp = NULL;
+ int fd;
+ FILE *fp;
time_t t = time(NULL);
- if (!(fp = fopen(c->outpath, "a")))
+ fd = openat(c->dfd, "out", O_CREAT | O_APPEND | O_WRONLY, S_IRWXU);
+ if (!(fp = fdopen(fd, "a")))
return;
fprintf(fp, "%lu %s\n", (unsigned long)t, buf);
fclose(fp);
@@ -836,7 +819,12 @@ main(int argc, char *argv[])
fprintf(stderr, "%s: path to irc directory too long\n", argv0);
exit(1);
}
- create_dirtree(ircpath);
+ dirfd = ensure_dirtree(ircpath);
+
+ if (dirfd < 0) {
+ fprintf(stderr, "%s: could not open irc directory\n", argv0);
+ exit(1);
+ }
channelmaster = channel_add(""); /* master channel */
if (key)
@@ -852,5 +840,7 @@ main(int argc, char *argv[])
channel_leave(c);
}
+ close(dirfd);
+
return 0;
}