commit 11c986679a15e976e5fdde5e439a18be4acac0de from: Omar Polo date: Sun Apr 25 12:06:54 2021 UTC sort the auto index alphabetically commit - bb4be662f1405805421361bb608b0defc6882cd2 commit + 11c986679a15e976e5fdde5e439a18be4acac0de blob - 3d4a53148a9b2a707fca264e47723762e707410d blob + 95c4089a502e2f56f18b752e6163b4fc3c3163be --- ChangeLog +++ ChangeLog @@ -1,3 +1,7 @@ +2021-04-25 Omar Polo + + * server.c (open_dir): sort the auto index alphabetically + 2021-04-21 Omar Polo * mime.c (load_default_mime): use `text/x-patch' for .patch and .diff files blob - 731d37edbc4bf55d205dce4b3ef062e98182988d blob + 16f3bbc22db226c20300675bf27b572ef3799d15 --- Makefile +++ Makefile @@ -13,7 +13,8 @@ lex.yy.c: lex.l y.tab.c y.tab.c: parse.y ${YACC} -b y -d parse.y -SRCS = gmid.c iri.c utf8.c ex.c server.c sandbox.c mime.c puny.c utils.c log.c +SRCS = gmid.c iri.c utf8.c ex.c server.c sandbox.c mime.c puny.c \ + utils.c log.c dirs.c OBJS = ${SRCS:.c=.o} lex.yy.o y.tab.o ${COMPAT} gmid: ${OBJS} blob - /dev/null blob + 7965f8feda2217275936a423aa8fe9390a69ee8d (mode 644) --- /dev/null +++ dirs.c @@ -0,0 +1,161 @@ +/* $OpenBSD: scandir.c,v 1.21 2019/06/28 13:32:41 deraadt Exp $ */ + +/* + * This is a modified verision of scandir that takes an explicit + * directory file descriptor as parameter. + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Scan the directory fd calling select to make a list of selected + * directory entries then sort using qsort and compare routine dcomp. + * Returns the number of entries and a pointer to a list of pointers to + * struct dirent (through namelist). Returns -1 if there were any errors. + */ + +#include "gmid.h" + +#include +#include + +#include +#include +#include +#include +#include + +#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) + +/* + * The DIRSIZ macro is the minimum record length which will hold the directory + * entry. This requires the amount of space in struct dirent without the + * d_name field, plus enough space for the name and a terminating nul byte + * (dp->d_namlen + 1), rounded up to a 4 byte boundary. + */ +#undef DIRSIZ +#define DIRSIZ(dp) \ + ((sizeof(struct dirent) - sizeof(dp)->d_name) + \ + (((dp)->d_namlen + 1 + 3) &~ 3)) + +int +scandir_fd(int fd, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*dcomp)(const struct dirent **, const struct dirent **)) +{ + struct dirent *d, *p, **names = NULL; + size_t /* arraysz, */ nitems = 0; + long arraysz; + struct stat stb; + DIR *dirp; + + if ((dirp = fdopendir(fd)) == NULL) + return (-1); + if (fstat(fd, &stb) == -1) + goto fail; + + /* + * estimate the array size by taking the size of the directory file + * and dividing it by a multiple of the minimum size entry. + */ + arraysz = MAXIMUM(stb.st_size / 24, 16); + if (arraysz > SIZE_MAX / sizeof(struct dirent *)) { + errno = ENOMEM; + goto fail; + } + names = calloc(arraysz, sizeof(struct dirent *)); + if (names == NULL) + goto fail; + + while ((d = readdir(dirp)) != NULL) { + if (select != NULL && !(*select)(d)) + continue; /* just selected names */ + + /* + * Check to make sure the array has space left and + * realloc the maximum size. + */ + if (nitems >= arraysz) { + struct dirent **nnames; + + if (fstat(fd, &stb) == -1) + goto fail; + + arraysz *= 2; + if (SIZE_MAX / sizeof(struct dirent *) < arraysz) + goto fail; + nnames = reallocarray(names, + arraysz, sizeof(struct dirent *)); + if (nnames == NULL) + goto fail; + + names = nnames; + } + + /* + * Make a minimum size copy of the data + */ + p = malloc(DIRSIZ(d)); + if (p == NULL) + goto fail; + + p->d_ino = d->d_ino; + p->d_type = d->d_type; + p->d_reclen = d->d_reclen; + p->d_namlen = d->d_namlen; + bcopy(d->d_name, p->d_name, p->d_namlen + 1); + names[nitems++] = p; + } + closedir(dirp); + if (nitems && dcomp != NULL) + qsort(names, nitems, sizeof(struct dirent *), + (int(*)(const void *, const void *))dcomp); + *namelist = names; + return (nitems); + +fail: + while (nitems > 0) + free(names[--nitems]); + free(names); + closedir(dirp); + return (-1); +} + +int +select_non_dot(const struct dirent *d) +{ + return strcmp(d->d_name, "."); +} + +int +select_non_dotdot(const struct dirent *d) +{ + return strcmp(d->d_name, ".") && strcmp(d->d_name, ".."); +} blob - cacae398d880cb8e50d0a37d3b6fe80125bd9274 blob + 6f4223ececf4a54382efe524370eda3d75537e7f --- gmid.h +++ gmid.h @@ -180,7 +180,8 @@ struct client { int code; const char *meta; int fd, pfd; - DIR *dir; + struct dirent **dir; + int dirlen, diroff; /* big enough to store STATUS + SPACE + META + CRLF */ char sbuf[1029]; @@ -285,6 +286,12 @@ int vhost_disable_log(struct vhost*, const char*); void mark_nonblock(int); void loop(struct tls*, int, int, struct imsgbuf*); +/* dirs.c */ +int scandir_fd(int, struct dirent***, int(*)(const struct dirent*), + int(*)(const struct dirent**, const struct dirent**)); +int select_non_dot(const struct dirent*); +int select_non_dotdot(const struct dirent*); + /* ex.c */ int send_string(int, const char*); int recv_string(int, char**); blob - 47b1bb0d5e4c0e1ce5f613bbf3d3b0a1304910c4 blob + 86d007a0bd5418a473a1444d43663fd443306922 --- server.c +++ server.c @@ -723,8 +723,10 @@ static void open_dir(struct client *c) { size_t len; - int dirfd; + int dirfd, root; char *before_file; + + root = !strcmp(c->iri.path, "/") || *c->iri.path == '\0'; len = strlen(c->iri.path); if (len > 0 && !ends_with(c->iri.path, "/")) { @@ -779,12 +781,16 @@ open_dir(struct client *c) c->pfd = dirfd; c->next = enter_handle_dirlist; - if ((c->dir = fdopendir(c->pfd)) == NULL) { - log_err(c, "fdopendir(%d) (vhost:%s) %s: %s", + c->dirlen = scandir_fd(c->pfd, &c->dir, + root ? select_non_dotdot : select_non_dot, + alphasort); + if (c->dirlen == -1) { + log_err(c, "scandir_fd(%d) (vhost:%s) %s: %s", c->pfd, c->host->domain, c->iri.path, strerror(errno)); start_reply(c, TEMP_FAILURE, "internal server error"); return; } + c->diroff = 0; c->off = 0; start_reply(c, SUCCESS, "text/gemini"); @@ -867,23 +873,18 @@ handle_dirlist(int fd, short ev, void *d) static int read_next_dir_entry(struct client *c) { - struct dirent *d; + if (c->diroff == c->dirlen) + return 0; - do { - errno = 0; - if ((d = readdir(c->dir)) == NULL) { - if (errno != 0) - log_err(c, "readdir: %s", strerror(errno)); - return 0; - } - } while (!strcmp(d->d_name, ".")); - /* XXX: url escape */ - snprintf(c->sbuf, sizeof(c->sbuf), "=> %s %s\n", - d->d_name, d->d_name); + snprintf(c->sbuf, sizeof(c->sbuf), "=> %s\n", + c->dir[c->diroff]->d_name); + + free(c->dir[c->diroff]); + c->diroff++; + c->len = strlen(c->sbuf); c->off = 0; - return 1; } @@ -1027,7 +1028,7 @@ close_conn(int fd, short ev, void *d) close(c->pfd); if (c->dir != NULL) - closedir(c->dir); + free(c->dir); close(c->fd); c->fd = -1;