commit f28f9311393eb43145c15dae01a440f1d0c9064c from: Omar Polo date: Wed Oct 14 08:09:06 2020 UTC switching to mmap() based file handling commit - 9c56b0a78a8194849c8d3b0f3e9727407b03dda0 commit + f28f9311393eb43145c15dae01a440f1d0c9064c blob - 741d9e7eb62711e0f234c8679bd35404494d7561 blob + c82e9e4dd6399c72e5819e976302740aa49c7db8 --- gmid.c +++ gmid.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include @@ -44,8 +45,6 @@ /* large enough to hold a copy of a gemini URL and still have extra room */ #define PATHBUF 2048 -#define FILEBUF 1024 - #define SUCCESS 20 #define NOT_FOUND 51 #define BAD_REQUEST 59 @@ -63,10 +62,12 @@ enum { struct client { struct tls *ctx; - int state; - int code; + int state; + int code; const char *meta; - int fd; + int fd; + void *buf, *i; + ssize_t len, off; }; struct etm { /* file extension to mime */ @@ -97,11 +98,13 @@ char *url_start_of_request(char*); int url_trim(char*); void adjust_path(char*); int path_isdir(char*); +ssize_t filesize(int); int start_reply(struct pollfd*, struct client*, int, const char*); int isdir(int); const char *path_ext(const char*); const char *mime(const char*); +int open_file(char*, struct pollfd*, struct client*); void send_file(char*, struct pollfd*, struct client*); void send_dir(char*, struct pollfd*, struct client*); void handle(struct pollfd*, struct client*); @@ -174,7 +177,7 @@ adjust_path(char *path) char *s; size_t len; - /* /.. -> / */ + /* /.. -> / */ len = strlen(path); if (len >= 3) { if (!strcmp(&path[len-3], "/..")) { @@ -244,6 +247,18 @@ isdir(int fd) return S_ISDIR(sb.st_mode); } +ssize_t +filesize(int fd) +{ + ssize_t len; + + if ((len = lseek(fd, 0, SEEK_END)) == -1) + return -1; + if (lseek(fd, 0, SEEK_SET) == -1) + return -1; + return len; +} + const char * path_ext(const char *path) { @@ -276,77 +291,88 @@ mime(const char *path) return def; } -void -send_file(char *path, struct pollfd *fds, struct client *client) +int +open_file(char *path, struct pollfd *fds, struct client *c) { char fpath[PATHBUF]; - char buf[FILEBUF]; - size_t off; - ssize_t ret, len; - if (client->fd == -1) { - assert(path != NULL); + assert(path != NULL); - bzero(fpath, sizeof(fpath)); + bzero(fpath, sizeof(fpath)); - if (*path != '.') - fpath[0] = '.'; - strlcat(fpath, path, PATHBUF); + if (*path != '.') + fpath[0] = '.'; + strlcat(fpath, path, PATHBUF); - if ((client->fd = openat(dirfd, fpath, O_RDONLY | O_NOFOLLOW)) == -1) { - warn("open: %s", fpath); - if (!start_reply(fds, client, NOT_FOUND, "not found")) - return; - goodbye(fds, client); - return; - } + if ((c->fd = openat(dirfd, fpath, O_RDONLY | O_NOFOLLOW)) == -1) { + warn("open: %s", fpath); + if (!start_reply(fds, c, NOT_FOUND, "not found")) + return 0; + goodbye(fds, c); + return 0; + } - if (isdir(client->fd)) { - warnx("%s is a directory, trying %s/index.gmi", fpath, fpath); - close(client->fd); - client->fd = -1; - send_dir(fpath, fds, client); - return; - } + if (isdir(c->fd)) { + warnx("%s is a directory, trying %s/index.gmi", fpath, fpath); + close(c->fd); + c->fd = -1; + send_dir(fpath, fds, c); + return 0; + } - if (!start_reply(fds, client, SUCCESS, mime(fpath))) - return; + if ((c->len = filesize(c->fd)) == -1) { + warn("filesize: %s", fpath); + goodbye(fds, c); + return 0; + } + + if ((c->buf = mmap(NULL, c->len, PROT_READ, MAP_PRIVATE, + c->fd, 0)) == MAP_FAILED) { + warn("mmap: %s", fpath); + goodbye(fds, c); + return 0; } + c->i = c->buf; - while (1) { - len = read(client->fd, buf, sizeof(buf)); - if (len == -1) - warn("read"); - if (len == 0 || len == -1) { - goodbye(fds, client); - return; - } + return start_reply(fds, c, SUCCESS, mime(fpath)); +} - off = 0; - while (len > 0) { - ret = tls_write(client->ctx, buf+off, len); - switch (ret) { - case -1: - warnx("tls_write: %s", tls_error(client->ctx)); - goodbye(fds, client); - return; +void +send_file(char *path, struct pollfd *fds, struct client *c) +{ + ssize_t ret, len; - case TLS_WANT_POLLIN: - case TLS_WANT_POLLOUT: - fds->events = ret == TLS_WANT_POLLIN ? POLLIN : POLLOUT; - if (lseek(client->fd, -1 * len, SEEK_CUR) == -1) { - warnx("lseek"); - goodbye(fds, client); - } - return; + if (c->fd == -1) { + if (!open_file(path, fds, c)) + return; + c->state = S_SENDING; + } - default: - off += ret; - len -= ret; - break; - } + len = (c->buf + c->len) - c->i; + + while (len > 0) { + switch (ret = tls_write(c->ctx, c->i, len)) { + case -1: + warnx("tls_write: %s", tls_error(c->ctx)); + goodbye(fds, c); + return; + + case TLS_WANT_POLLIN: + fds->events = POLLIN; + return; + + case TLS_WANT_POLLOUT: + fds->events = POLLOUT; + return; + + default: + c->i += ret; + len -= ret; + break; } } + + goodbye(fds, c); } void @@ -467,7 +493,7 @@ make_socket(int port) struct sockaddr_in addr; if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) - err(1, "socket"); + err(1, "socket"); v = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) == -1) @@ -485,10 +511,10 @@ make_socket(int port) addr.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) - err(1, "bind"); + err(1, "bind"); if (listen(sock, 16) == -1) - err(1, "listen"); + err(1, "listen"); return sock; } @@ -520,6 +546,7 @@ do_accept(int sock, struct tls *ctx, struct pollfd *fd clients[i].state = S_OPEN; clients[i].fd = -1; + clients[i].buf = MAP_FAILED; return; } @@ -548,6 +575,9 @@ goodbye(struct pollfd *pfd, struct client *c) tls_free(c->ctx); c->ctx = NULL; + if (c->buf != MAP_FAILED) + munmap(c->buf, c->len); + if (c->fd != -1) close(c->fd); @@ -571,7 +601,7 @@ loop(struct tls *ctx, int sock) fds[0].fd = sock; for (;;) { - if ((todo = poll(fds, MAX_USERS, INFTIM)) == -1) + if ((todo = poll(fds, MAX_USERS, INFTIM)) == -1) err(1, "poll"); for (i = 0; i < MAX_USERS; i++) {