ii

My fork of https://tools.suckless.org/ii/
git clone https://git.inz.fi/ii
Log | Files | Refs | README | LICENSE

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:
Mii.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; }