From 35f3191e4c5b3c7d0df425c7c8b82fea4df66b74 Mon Sep 17 00:00:00 2001 From: tiptorrent development team Date: Thu, 23 Dec 2021 16:27:06 +0100 Subject: use fallocate() to store the chunks in the file The client sends a HTTP HEAD request to the server to check that the file exists and to get the file size, then it calls open() to create the file and fallocate() to preallocate the bytes. The client calculates the offset based on the chunk number and it calls lseek(). --- src/main.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/main.c b/src/main.c index 8678cf6..f6334e0 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,7 @@ #include #include #include +#include #define TIP_TORRENT_PORT 9999 @@ -43,6 +44,7 @@ enum { TIP_CLIENT_GET_HEADER, TIP_CLIENT_GET_PAYLOAD, TIP_CLIENT_POST_REDIRECT, + TIP_CLIENT_HEAD_HEADER, TIP_CLIENT_DONE, }; @@ -53,6 +55,7 @@ struct tip_client { uint32_t buf_len; uint64_t data_len; uint64_t content_len; + uint64_t chunk_offset; int state; int num_retries; int fd; @@ -188,13 +191,7 @@ static int tip_client_get_hdr(struct tip_client *cli) else tip_client_stats.direct_from_server++; - cli->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (cli->fd < 0) { - syslog(LOG_ERR, "failed to open file %s: %s", - filename, strerror(errno)); - return -1; - } - + lseek(cli->fd, cli->chunk_offset, SEEK_SET); header_len = trailer - cli->buf; payload = cli->buf + header_len; payload_len = cli->buf_len - header_len; @@ -247,6 +244,50 @@ static int tip_client_get_payload(struct tip_client *cli) return 1; } +static int tip_client_head_hdr(struct tip_client *cli) +{ + char *ptr; + + ptr = strstr(cli->buf, "\r\n\r\n"); + if (!ptr) + return 0; + + if (!strncmp(cli->buf, "HTTP/1.1 404 Not Found", strlen("HTTP/1.1 404 Not Found"))) { + syslog(LOG_ERR, "server says file `%s' not found\n", filename); + return -1; + } + + ptr = strstr(cli->buf, "Content-Length: "); + if (!ptr) + return -1; + + if (sscanf(ptr, "Content-Length: %lu[^\r\n]", &cli->content_len) != 1) + return -1; + if (cli->content_len < 0) + return -1; + if (cli->content_len == 0) { + syslog(LOG_ERR, "server reports zero size file %s", filename); + return -1; + } + + cli->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (cli->fd < 0) { + syslog(LOG_ERR, "failed to open file %s: %s", + filename, strerror(errno)); + return -1; + } + + if (posix_fallocate(cli->fd, 0, cli->content_len) < 0) { + syslog(LOG_ERR, "failed to allocate room for file %s: %s", + filename, strerror(errno)); + return -1; + } + + cli->state = TIP_CLIENT_DONE; + + return 1; +} + static int tip_client_post_redirect(struct tip_client *cli) { char *ptr; @@ -310,6 +351,13 @@ static void tip_client_read_cb(struct ev_loop *loop, struct ev_io *io, int event if (ret == 0) goto close; break; + case TIP_CLIENT_HEAD_HEADER: + ret = tip_client_head_hdr(cli); + if (ret < 0) + goto error; + if (!ret) + return; + goto close; case TIP_CLIENT_POST_REDIRECT: ret = tip_client_post_redirect(cli); if (ret < 0) @@ -348,12 +396,23 @@ static void tip_client_connect_cb(struct ev_loop *loop, struct ev_io *io, int ev return; } - syslog(LOG_INFO, "connected to %s to fetch file %s\n", addr, filename); - - if (cli->state == TIP_CLIENT_POST_REDIRECT) - snprintf(buf, sizeof(buf), "POST /%s HTTP/1.1\r\n\r\n", filename); - else + switch (cli->state) { + case TIP_CLIENT_GET_HEADER: + syslog(LOG_INFO, "connected to %s to fetch file %s\n", + inet_ntoa(cli->addr.sin_addr), filename); snprintf(buf, sizeof(buf), "GET /%s HTTP/1.1\r\n\r\n", filename); + break; + case TIP_CLIENT_HEAD_HEADER: + syslog(LOG_INFO, "connected to %s to get file size of %s\n", + inet_ntoa(cli->addr.sin_addr), filename); + snprintf(buf, sizeof(buf), "HEAD /%s HTTP/1.1\r\n\r\n", filename); + break; + case TIP_CLIENT_POST_REDIRECT: + syslog(LOG_INFO, "connected to %s to report redirection for %s\n", + inet_ntoa(cli->addr.sin_addr), filename); + snprintf(buf, sizeof(buf), "POST /%s HTTP/1.1\r\n\r\n", filename); + break; + } ret = send(cli->io.fd, buf, strlen(buf), 0); if (ret < 0) { @@ -472,9 +531,10 @@ static char _filename[PATH_MAX + 1]; int main(int argc, char *argv[]) { struct timeval tv_start, tv_stop, tv; + uint64_t data_len = 0, file_size = 0; bool file_chunk[MAX_CHUNKS] = {}; - uint64_t data_len = 0; - int i, k; + uint64_t chunk_size; + int i, k, fd; if (argc != 3) { printf("%s [ip] [file]\n", argv[0]); @@ -490,12 +550,26 @@ int main(int argc, char *argv[]) gettimeofday(&tv_start, NULL); + do { + filename = argv[2]; + _cli.state = TIP_CLIENT_HEAD_HEADER; + } while (tip_client_request_file(&_cli, addr, filename) > 0); + + if (_cli.state != TIP_CLIENT_DONE) + goto err; + + fd = _cli.fd; + file_size = _cli.content_len; + for (i = 0; i < MAX_CHUNKS; i++) { memset(&_cli, 0, sizeof(_cli)); k = select_file_chunk(file_chunk); snprintf(_filename, sizeof(_filename), "%s.%u", argv[2], k); filename = _filename; + chunk_size = file_size / MAX_CHUNKS; + _cli.chunk_offset = chunk_size * k; + _cli.fd = fd; do { syslog(LOG_INFO, "Requesting file %s to server\n", filename); @@ -507,7 +581,7 @@ int main(int argc, char *argv[]) file_chunk[k] = true; data_len += _cli.data_len; } - +err: gettimeofday(&tv_stop, NULL); timersub(&tv_stop, &tv_start, &tv); -- cgit v1.2.3-18-g5258