diff options
| author | Richard Nyberg <rnyberg@murmeldjur.se> | 2005-06-24 09:51:38 +0000 |
|---|---|---|
| committer | Richard Nyberg <rnyberg@murmeldjur.se> | 2005-06-24 09:51:38 +0000 |
| commit | dd0d462afae75ff243f8cd1528963f9ad489706d (patch) | |
| tree | 2ef874a1fe5212245814d16f4c9b389524aed9d1 /misc | |
| download | btpd-dd0d462afae75ff243f8cd1528963f9ad489706d.tar.gz btpd-dd0d462afae75ff243f8cd1528963f9ad489706d.zip | |
Import btpd-0.1.
git-svn-id: file:///home/rnyberg/svngit/btpd/releases/0.1@1 76a1f634-46fa-0310-9943-bd1476092a85
Diffstat (limited to 'misc')
| -rw-r--r-- | misc/Makefile.am | 8 | ||||
| -rw-r--r-- | misc/benc.c | 345 | ||||
| -rw-r--r-- | misc/benc.h | 37 | ||||
| -rw-r--r-- | misc/iobuf.c | 64 | ||||
| -rw-r--r-- | misc/iobuf.h | 15 | ||||
| -rw-r--r-- | misc/metainfo.c | 275 | ||||
| -rw-r--r-- | misc/metainfo.h | 28 | ||||
| -rw-r--r-- | misc/stream.c | 241 | ||||
| -rw-r--r-- | misc/stream.h | 36 | ||||
| -rw-r--r-- | misc/subr.c | 141 | ||||
| -rw-r--r-- | misc/subr.h | 21 |
11 files changed, 1211 insertions, 0 deletions
diff --git a/misc/Makefile.am b/misc/Makefile.am new file mode 100644 index 0000000..38af57a --- /dev/null +++ b/misc/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES=libmisc.a +libmisc_a_SOURCES=\ + benc.c benc.h\ + stream.c stream.h\ + subr.c subr.h\ + metainfo.c metainfo.h\ + iobuf.c iobuf.h +libmisc_a_CPPFLAGS=@openssl_CPPFLAGS@ diff --git a/misc/benc.c b/misc/benc.c new file mode 100644 index 0000000..632b0fd --- /dev/null +++ b/misc/benc.c @@ -0,0 +1,345 @@ +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> + +#include "benc.h" + +#define benc_safeset(out, val) if ((out) != NULL) *(out) = (val) + +static const char *benc_validate_aux(const char *p, const char *end); + +int +benc_validate(const char *p, size_t len) +{ + const char *end = p + len - 1; + + if (len <= 0) + return EINVAL; + + return benc_validate_aux(p, end) == end ? 0 : EINVAL; +} + +static const char * +benc_validate_aux(const char *p, const char *end) +{ + size_t d = 0; + switch (*p) { + case 'd': + d = 1; + case 'l': + for (p++; p <= end && *p != 'e'; p++) { + if (d != 0) { + if (d % 2 == 1 && !isdigit(*p)) + return NULL; + else + d++; + } + if ((p = benc_validate_aux(p, end)) == NULL) + return NULL; + } + if (p > end || (d != 0 && d % 2 != 1)) + return NULL; + break; + case 'i': + p++; + if (p > end) + return NULL; + if (*p == '-') + p++; + if (p > end || !isdigit(*p)) + return NULL; + p++; + while (p <= end && isdigit(*p)) + p++; + if (p > end || *p != 'e') + return NULL; + break; + default: + if (isdigit(*p)) { + size_t len = 0; + while (p <= end && isdigit(*p)) { + len *= 10; + len += *p - '0'; + p++; + } + if (p <= end && *p == ':' && p + len <= end) + p += len; + else + return NULL; + } + else + return NULL; + break; + } + return p; +} + +size_t +benc_length(const char *p) +{ + size_t blen; + const char *next; + + switch (*p) { + case 'd': + case 'l': + blen = 2; // [l|d]...e + next = benc_first(p); + while (*next != 'e') { + size_t len = benc_length(next); + blen += len; + next += len; + } + return blen; + case 'i': + for (next = p + 1; *next != 'e'; next++) + ; + return next - p + 1; + default: + assert(benc_str(p, &next, &blen, NULL) == 0); + return next - p + blen; + } +} + +size_t +benc_nelems(const char *p) +{ + size_t nelems = 0; + for (p = benc_first(p); p != NULL; p = benc_next(p)) + nelems++; + return nelems; +} + +const char * +benc_first(const char *p) +{ + assert(benc_islst(p)); + return *(p + 1) == 'e' ? NULL : p + 1; +} + +const char * +benc_next(const char *p) +{ + size_t blen = benc_length(p); + return *(p + blen) == 'e' ? NULL : p + blen; +} + +int +benc_str(const char *p, const char **out, size_t *len, const char**next) +{ + size_t blen = 0; + assert(isdigit(*p)); + blen = *p - '0'; + p++; + while (isdigit(*p)) { + blen *= 10; + blen += *p - '0'; + p++; + } + assert(*p == ':'); + benc_safeset(len, blen); + benc_safeset(out, p + 1); + benc_safeset(next, *(p + blen + 1) == 'e' ? NULL : p + blen + 1); + return 0; +} + +int +benc_strz(const char *p, char **out, size_t *len, const char **next) +{ + int err; + size_t blen; + const char *bstr; + + if ((err = benc_str(p, &bstr, &blen, next)) == 0) { + if ((*out = malloc(blen + 1)) != NULL) { + memcpy(*out, bstr, blen); + (*out)[blen] = '\0'; + benc_safeset(len, blen); + } else + err = ENOMEM; + } + return err; +} + +int +benc_stra(const char *p, char **out, size_t *len, const char **next) +{ + int err; + size_t blen; + const char *bstr; + + if ((err = benc_str(p, &bstr, &blen, next)) == 0) { + if ((*out = malloc(blen)) != NULL) { + memcpy(*out, bstr, blen); + benc_safeset(len, blen); + } else + err = ENOMEM; + } + return err; +} + +int +benc_int64(const char *p, int64_t *out, const char **next) +{ + int sign = 1; + int64_t res = 0; + + assert(*p == 'i'); + p++; + if (*p == '-') { + sign = -1; + p++; + } + assert(isdigit(*p)); + res += sign * (*p - '0'); + p++; + while (isdigit(*p)) { + res *= sign * 10; + res += sign * (*p - '0'); + p++; + } + assert(*p == 'e'); + benc_safeset(out, res); + benc_safeset(next, *(p + 1) == 'e' ? NULL : p + 1); + + return 0; +} + +int +benc_uint32(const char *p, uint32_t *out, const char **next) +{ + int err; + int64_t res; + if ((err = benc_int64(p, &res, next)) == 0) { + if (res >= 0 && res <= 0xffffffffUL) + *out = (uint32_t)res; + else + err = EINVAL; + } + return err; +} + +int +benc_dget_any(const char *p, const char *key, const char **val) +{ + int res; + size_t len, blen; + const char *bstr; + + assert(benc_isdct(p)); + + len = strlen(key); + + p = benc_first(p); + while (p != NULL) { + if ((res = benc_str(p, &bstr, &blen, &p)) != 0) + return res; + + res = strncmp(bstr, key, blen); + if (res == 0 && len == blen) { + *val = p; + return 0; + } else if (res <= 0) { + p = benc_next(p); + } else + return ENOENT; + } + return ENOENT; +} + +int +benc_dget_lst(const char *p, const char *key, const char **val) +{ + int err; + if ((err = benc_dget_any(p, key, val)) == 0) + if (!benc_islst(*val)) + err = EINVAL; + return err; +} + +int +benc_dget_dct(const char *p, const char *key, const char **val) +{ + int err; + if ((err = benc_dget_any(p, key, val)) == 0) + if (!benc_isdct(*val)) + err = EINVAL; + return err; +} + +int +benc_dget_str(const char *p, const char *key, const char **val, size_t *len) +{ + int err; + const char *sp; + if ((err = benc_dget_any(p, key, &sp)) == 0) + err = benc_isstr(sp) ? benc_str(sp, val, len, NULL) : EINVAL; + return err; +} + +int +benc_dget_stra(const char *p, const char *key, char **val, size_t *len) +{ + int err; + const char *sp; + if ((err = benc_dget_any(p, key, &sp)) == 0) + err = benc_isstr(sp) ? benc_stra(sp, val, len, NULL) : EINVAL; + return err; +} + +int +benc_dget_strz(const char *p, const char *key, char **val, size_t *len) +{ + int err; + const char *sp; + if ((err = benc_dget_any(p, key, &sp)) == 0) + err = benc_isstr(sp) ? benc_strz(sp, val, len, NULL) : EINVAL; + return err; +} + +int +benc_dget_int64(const char *p, const char *key, int64_t *val) +{ + int err; + const char *ip; + if ((err = benc_dget_any(p, key, &ip)) == 0) + err = benc_isint(ip) ? benc_int64(ip, val, NULL) : EINVAL; + return err; +} + +int +benc_dget_uint32(const char *p, const char *key, uint32_t *val) +{ + int err; + const char *ip; + if ((err = benc_dget_any(p, key, &ip)) == 0) + err = benc_isint(ip) ? benc_uint32(ip, val, NULL) : EINVAL; + return err; +} + +int +benc_islst(const char *p) +{ + return *p == 'l' || *p == 'd'; +} + +int +benc_isdct(const char *p) +{ + return *p == 'd'; +} + +int +benc_isint(const char *p) +{ + return *p == 'i'; +} + +int +benc_isstr(const char *p) +{ + return isdigit(*p); +} diff --git a/misc/benc.h b/misc/benc.h new file mode 100644 index 0000000..0cc3873 --- /dev/null +++ b/misc/benc.h @@ -0,0 +1,37 @@ +#ifndef BTPD_BENC_H +#define BTPD_BENC_H + +int benc_validate(const char *p, size_t len); + +size_t benc_length(const char *p); +size_t benc_nelems(const char *p); + +const char *benc_first(const char *p); +const char *benc_next(const char *p); + +int benc_str(const char *p, const char **mem, size_t *len, const char**next); +int benc_stra(const char *p, char **out, size_t *len, const char **next); +int benc_strz(const char *p, char **out, size_t *len, const char **next); +int benc_int64(const char *p, int64_t *out, const char **next); +int benc_uint32(const char *p, uint32_t *out, const char **next); + +#define benc_off benc_int64 + +int benc_dget_any(const char *p, const char *key, const char **val); +int benc_dget_lst(const char *p, const char *key, const char **val); +int benc_dget_dct(const char *p, const char *key, const char **val); +int benc_dget_str(const char *p, const char *key, + const char **val, size_t *len); +int benc_dget_stra(const char *p, const char *key, char **val, size_t *len); +int benc_dget_strz(const char *p, const char *key, char **val, size_t *len); +int benc_dget_int64(const char *p, const char *key, int64_t *val); +int benc_dget_uint32(const char *p, const char *key, uint32_t *val); + +#define benc_dget_off benc_dget_int64 + +int benc_islst(const char *p); +int benc_isdct(const char *p); +int benc_isint(const char *p); +int benc_isstr(const char *p); + +#endif diff --git a/misc/iobuf.c b/misc/iobuf.c new file mode 100644 index 0000000..a7304c6 --- /dev/null +++ b/misc/iobuf.c @@ -0,0 +1,64 @@ +#include <errno.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "iobuf.h" + +#define GROWLEN (1 << 14) + +int +buf_init(struct io_buffer *iob, size_t size) +{ + iob->buf_off = 0; + iob->buf_len = size; + iob->buf = malloc(size); + if (iob->buf == NULL) + return ENOMEM; + else + return 0; +} + +int +buf_grow(struct io_buffer *iob, size_t addlen) +{ + char *nbuf = realloc(iob->buf, iob->buf_len + addlen); + if (nbuf == NULL) + return ENOMEM; + else { + iob->buf = nbuf; + iob->buf_len += addlen; + return 0; + } +} + +int +buf_print(struct io_buffer *iob, const char *fmt, ...) +{ + int np; + va_list ap; + va_start(ap, fmt); + np = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + while (np + 1 > iob->buf_len - iob->buf_off) + if (buf_grow(iob, GROWLEN) != 0) + return ENOMEM; + va_start(ap, fmt); + vsnprintf(iob->buf + iob->buf_off, np + 1, fmt, ap); + va_end(ap); + iob->buf_off += np; + return 0; +} + +int +buf_write(struct io_buffer *iob, const void *buf, size_t len) +{ + while (iob->buf_len - iob->buf_off < len) + if (buf_grow(iob, GROWLEN) != 0) + return ENOMEM; + bcopy(buf, iob->buf + iob->buf_off, len); + iob->buf_off += len; + return 0; +} diff --git a/misc/iobuf.h b/misc/iobuf.h new file mode 100644 index 0000000..ef213d1 --- /dev/null +++ b/misc/iobuf.h @@ -0,0 +1,15 @@ +#ifndef BTPD_IOBUF_H +#define BTPD_IOBUF_H + +struct io_buffer { + size_t buf_off; + size_t buf_len; + char *buf; +}; + +int buf_init(struct io_buffer *iob, size_t size); +int buf_grow(struct io_buffer *iob, size_t size); +int buf_write(struct io_buffer *iob, const void *data, size_t size); +int buf_print(struct io_buffer *iob, const char *fmt, ...); + +#endif diff --git a/misc/metainfo.c b/misc/metainfo.c new file mode 100644 index 0000000..61da727 --- /dev/null +++ b/misc/metainfo.c @@ -0,0 +1,275 @@ +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/sha.h> + +#include "benc.h" +#include "metainfo.h" +#include "subr.h" + +/* + * d + * announce = url + * info = d + * name = advisory file/dir save name + * piece length = power of two length of each block + * pieces = 20b of sha1-hash * num of pieces + * length = length of file in bytes in single file download + * files = l d + * length = length of file in bytes + * path = l path components + * + */ + +#ifndef PRId64 +#define PRId64 "lld" +#endif +#ifndef PRIu32 +#define PRIu32 "u" +#endif + +void +print_metainfo(struct metainfo *tp) +{ + unsigned i; + + printf("Info hash: "); + for (i = 0; i < 20; i++) + printf("%.2x", tp->info_hash[i]); + printf("\n"); + printf("Tracker URL: %s\n", tp->announce); + printf("Piece length: %" PRId64 "\n", (int64_t)tp->piece_length); + printf("Number of pieces: %" PRIu32 "\n", tp->npieces); + printf("Number of files: %u\n", tp->nfiles); + printf("Advisory name: %s\n", tp->name); + printf("Files:\n"); + for (i = 0; i < tp->nfiles; i++) { + printf("%s (%" PRId64 ")\n", + tp->files[i].path, (int64_t)tp->files[i].length); + } + printf("Total length: %" PRId64 "\n\n", (int64_t)tp->total_length); +} + +static int +check_path(const char *path, size_t len) +{ + if (len == 0) + return 0; + else if (len == 1 && path[0] == '.') + return 0; + else if (len == 2 && path[0] == '.' && path[1] == '.') + return 0; + else if (memchr(path, '/', len) != NULL) + return 0; + return 1; +} + +int +fill_fileinfo(const char *fdct, struct fileinfo *tfp) +{ + int err; + size_t npath, plen, len; + const char *plst, *iter, *str; + + if ((err = benc_dget_off(fdct, "length", &tfp->length)) != 0) + return err; + + if ((err = benc_dget_lst(fdct, "path", &plst)) != 0) + return err; + + npath = plen = 0; + iter = benc_first(plst); + while (iter != NULL) { + if (!benc_isstr(iter)) + return EINVAL; + benc_str(iter, &str, &len, &iter); + if (!check_path(str, len)) + return EINVAL; + npath++; + plen += len; + } + if (npath == 0) + return EINVAL; + + if ((tfp->path = malloc(plen + (npath - 1) + 1)) == NULL) + return ENOMEM; + + iter = benc_first(plst); + benc_str(iter, &str, &len, &iter); + memcpy(tfp->path, str, len); + plen = len; + npath--; + while (npath > 0) { + tfp->path[plen++] = '/'; + benc_str(iter, &str, &len, &iter); + memcpy(tfp->path + plen, str, len); + plen += len; + npath--; + } + tfp->path[plen] = '\0'; + return 0; +} + +void +clear_metainfo(struct metainfo *mip) +{ + int i; + if (mip->piece_hash != NULL) + free(mip->piece_hash); + if (mip->announce != NULL) + free(mip->announce); + if (mip->files != NULL) { + for (i = 0; i < mip->nfiles; i++) { + if (mip->files[i].path != NULL) + free(mip->files[i].path); + } + free(mip->files); + } + if (mip->name != NULL) + free(mip->name); +} + +int +fill_metainfo(const char *bep, struct metainfo *tp, int mem_hashes) +{ + size_t len; + int err; + const char *base_addr = bep; + const char *hash_addr; + + if (!benc_isdct(bep)) + return EINVAL; + + if ((err = benc_dget_strz(bep, "announce", &tp->announce, NULL)) != 0) + goto out; + + if ((err = benc_dget_dct(bep, "info", &bep)) != 0) + goto out; + + SHA1(bep, benc_length(bep), tp->info_hash); + + if ((err = benc_dget_off(bep, "piece length", &tp->piece_length)) != 0) + goto out; + + if ((err = benc_dget_str(bep, "pieces", &hash_addr, &len)) != 0) + goto out; + + if (len % 20 != 0) { + err = EINVAL; + goto out; + } + tp->npieces = len / 20; + + tp->pieces_off = hash_addr - base_addr; + + if (mem_hashes) { + if ((tp->piece_hash = malloc(len)) == NULL) { + err = ENOMEM; + goto out; + } + bcopy(hash_addr, tp->piece_hash, len); + } + + if ((err = benc_dget_strz(bep, "name", &tp->name, NULL)) != 0) + goto out; + + err = benc_dget_off(bep, "length", &tp->total_length); + if (err == 0) { + tp->nfiles = 1; + tp->files = calloc(1, sizeof(struct fileinfo)); + if (tp->files != NULL) { + tp->files[0].length = tp->total_length; + tp->files[0].path = strdup(tp->name); + if (tp->files[0].path == NULL) { + err = ENOMEM; + goto out; + } + } else { + err = ENOMEM; + goto out; + } + } + else if (err == ENOENT) { + int i; + const char *flst, *fdct; + + if ((err = benc_dget_lst(bep, "files", &flst)) != 0) + goto out; + + tp->nfiles = benc_nelems(flst); + if (tp->nfiles < 1) { + err = EINVAL; + goto out; + } + tp->files = calloc(tp->nfiles, sizeof(struct fileinfo)); + + tp->total_length = 0; + i = 0; + for (fdct = benc_first(flst); fdct != NULL; fdct = benc_next(fdct)) { + if (!benc_isdct(fdct)) { + err = EINVAL; + goto out; + } + + if ((err = fill_fileinfo(fdct, &tp->files[i])) != 0) + goto out; + + tp->total_length += tp->files[i].length; + i++; + } + } + else + goto out; +out: + if (err != 0) + clear_metainfo(tp); + + return err; +} + +int +load_metainfo(const char *path, off_t size, int mem_hashes, + struct metainfo **res) +{ + char *buf; + int fd, err = 0; + + if ((fd = open(path, O_RDONLY)) == -1) + return errno; + + if (size <= 0) { + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + return errno; + } else + size = sb.st_size; + } + + if ((buf = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) + err = errno; + close(fd); + + if (err == 0) + err = benc_validate(buf, size); + + if (err == 0) + if ((*res = calloc(1, sizeof(**res))) == NULL) + err = ENOMEM; + + if (err == 0) + if ((err = fill_metainfo(buf, *res, mem_hashes)) != 0) + free(*res); + + munmap(buf, size); + return err; +} diff --git a/misc/metainfo.h b/misc/metainfo.h new file mode 100644 index 0000000..242bab3 --- /dev/null +++ b/misc/metainfo.h @@ -0,0 +1,28 @@ +#ifndef BTPD_METAINFO_H +#define BTPD_METAINFO_H + +struct fileinfo { + char *path; + off_t length; +}; + +struct metainfo { + char *name; + char *announce; + uint8_t info_hash[20]; + uint8_t (*piece_hash)[20]; + unsigned pieces_off; + uint32_t npieces; + off_t piece_length; + off_t total_length; + unsigned nfiles; + struct fileinfo *files; +}; + +int fill_fileinfo(const char *fdct, struct fileinfo *fip); +int fill_metainfo(const char *base, struct metainfo *mip, int mem_hashes); +void clear_metainfo(struct metainfo *mip); +void print_metainfo(struct metainfo *mip); +int load_metainfo(const char *path, off_t size, int mem_hashes, struct metainfo **res); + +#endif diff --git a/misc/stream.c b/misc/stream.c new file mode 100644 index 0000000..ee98374 --- /dev/null +++ b/misc/stream.c @@ -0,0 +1,241 @@ +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#include <openssl/sha.h> + +#include "metainfo.h" +#include "subr.h" +#include "stream.h" + +struct bt_stream_ro * +bts_open_ro(struct metainfo *meta, off_t off, F_fdcb fd_cb, void *fd_arg) +{ + struct bt_stream_ro *bts = malloc(sizeof(*bts)); + if (bts == NULL) + return NULL; + + bts->meta = meta; + bts->fd_cb = fd_cb; + bts->fd_arg = fd_arg; + bts->t_off = 0; + bts->f_off = 0; + bts->index = 0; + bts->fd = -1; + bts_seek_ro(bts, off); + return bts; +} + +void +bts_seek_ro(struct bt_stream_ro *bts, off_t off) +{ + struct fileinfo *files = bts->meta->files; + + assert(off >= 0 && off <= bts->meta->total_length); + + if (bts->fd != -1) { + close(bts->fd); + bts->fd = -1; + } + + bts->t_off = off; + bts->index = 0; + + while (off >= files[bts->index].length) { + off -= files[bts->index].length; + bts->index++; + } + + bts->f_off = off; +} + +int +bts_read_ro(struct bt_stream_ro *bts, char *buf, size_t len) +{ + struct fileinfo *files = bts->meta->files; + size_t boff, wantread; + ssize_t didread; + + assert(bts->t_off + len <= bts->meta->total_length); + + boff = 0; + while (boff < len) { + if (bts->fd == -1) { + int err = + bts->fd_cb(files[bts->index].path, &bts->fd, bts->fd_arg); + if (err != 0) + return err; + if (bts->f_off != 0) + lseek(bts->fd, bts->f_off, SEEK_SET); + } + + wantread = min(len - boff, files[bts->index].length - bts->f_off); + again: + didread = read(bts->fd, buf + boff, wantread); + if (didread == -1) { + if (errno == EINTR) + goto again; + else + return errno; + } + + boff += didread; + bts->f_off += didread; + bts->t_off += didread; + if (bts->f_off == files[bts->index].length) { + close(bts->fd); + bts->fd = -1; + bts->f_off = 0; + bts->index++; + } + if (didread != wantread) + return ENOENT; + } + return 0; +} + +void +bts_close_ro(struct bt_stream_ro *bts) +{ + if (bts->fd != -1) + close(bts->fd); + free(bts); +} + +#define SHAFILEBUF (1 << 15) + +int +bts_sha(struct bt_stream_ro *bts, off_t length, uint8_t *hash) +{ + SHA_CTX ctx; + char buf[SHAFILEBUF]; + size_t wantread; + int err = 0; + + SHA1_Init(&ctx); + while (length > 0) { + wantread = min(length, SHAFILEBUF); + if ((err = bts_read_ro(bts, buf, wantread)) != 0) + break; + length -= wantread; + SHA1_Update(&ctx, buf, wantread); + } + SHA1_Final(hash, &ctx); + return err; +} + +int +bts_hashes(struct metainfo *meta, + F_fdcb fd_cb, + void (*cb)(uint32_t, uint8_t *, void *), + void *arg) +{ + int err = 0; + uint8_t hash[SHA_DIGEST_LENGTH]; + uint32_t piece; + struct bt_stream_ro *bts; + off_t plen = meta->piece_length; + off_t llen = meta->total_length % plen; + + if ((bts = bts_open_ro(meta, 0, fd_cb, arg)) == NULL) + return ENOMEM; + + for (piece = 0; piece < meta->npieces; piece++) { + if (piece < meta->npieces - 1) + err = bts_sha(bts, plen, hash); + else + err = bts_sha(bts, llen, hash); + + if (err == 0) + cb(piece, hash, arg); + else if (err == ENOENT) { + cb(piece, NULL, arg); + if (piece < meta->npieces - 1) + bts_seek_ro(bts, (piece + 1) * plen); + err = 0; + } else + break; + } + bts_close_ro(bts); + return err; +} + +struct bt_stream_wo * +bts_open_wo(struct metainfo *meta, off_t off, F_fdcb fd_cb, void *fd_arg) +{ + struct bt_stream_wo *bts = malloc(sizeof(*bts)); + if (bts == NULL) + return NULL; + + bts->meta = meta; + bts->fd_cb = fd_cb; + bts->fd_arg = fd_arg; + bts->t_off = 0; + bts->f_off = 0; + bts->index = 0; + bts->fd = -1; + bts_seek_ro((struct bt_stream_ro *)bts, off); + return bts; +} + +int +bts_write_wo(struct bt_stream_wo *bts, const char *buf, size_t len) +{ + struct fileinfo *files = bts->meta->files; + size_t boff, wantwrite; + ssize_t didwrite; + + assert(bts->t_off + len <= bts->meta->total_length); + + boff = 0; + while (boff < len) { + if (bts->fd == -1) { + int err = + bts->fd_cb(files[bts->index].path, &bts->fd, bts->fd_arg); + if (err != 0) + return err; + if (bts->f_off != 0) + lseek(bts->fd, bts->f_off, SEEK_SET); + } + + wantwrite = min(len - boff, files[bts->index].length - bts->f_off); + didwrite = write(bts->fd, buf + boff, wantwrite); + if (didwrite == -1) + return errno; + + boff += didwrite; + bts->f_off += didwrite; + bts->t_off += didwrite; + if (bts->f_off == files[bts->index].length) { + if (fsync(bts->fd) == -1) { + int err = errno; + close(bts->fd); + return err; + } + if (close(bts->fd) == -1) + return errno; + bts->fd = -1; + bts->f_off = 0; + bts->index++; + } + } + return 0; +} + +int +bts_close_wo(struct bt_stream_wo *bts) +{ + int err = 0; + if (bts->fd != -1) { + if (fsync(bts->fd) == -1) { + err = errno; + close(bts->fd); + } else if (close(bts->fd) == -1) + err = errno; + } + free(bts); + return err; +} diff --git a/misc/stream.h b/misc/stream.h new file mode 100644 index 0000000..f71e5c9 --- /dev/null +++ b/misc/stream.h @@ -0,0 +1,36 @@ +#ifndef BTPD_STREAM_H +#define BTPD_STREAM_H + +typedef int (*F_fdcb)(const char *, int *, void *); + +#define def_stream(name) \ +struct name {\ + struct metainfo *meta;\ + F_fdcb fd_cb;\ + void *fd_arg;\ + unsigned index;\ + off_t t_off;\ + off_t f_off;\ + int fd;\ +} + +def_stream(bt_stream_ro); + +struct bt_stream_ro * +bts_open_ro(struct metainfo *meta, off_t off, F_fdcb fd_cb, void *fd_arg); +int bts_read_ro(struct bt_stream_ro *bts, char *buf, size_t len); +void bts_seek_ro(struct bt_stream_ro *bts, off_t nbytes); +void bts_close_ro(struct bt_stream_ro *bts); + +def_stream(bt_stream_wo); + +struct bt_stream_wo * +bts_open_wo(struct metainfo *meta, off_t off, F_fdcb fd_cb, void *fd_arg); +int bts_write_wo(struct bt_stream_wo *bts, const char *buf, size_t len); +int bts_close_wo(struct bt_stream_wo *bts); + +int bts_sha(struct bt_stream_ro *bts, off_t length, uint8_t *hash); +int bts_hashes(struct metainfo *, F_fdcb fd_cb, + void (*cb)(uint32_t, uint8_t *, void *), void *arg); + +#endif diff --git a/misc/subr.c b/misc/subr.c new file mode 100644 index 0000000..abbf4e9 --- /dev/null +++ b/misc/subr.c @@ -0,0 +1,141 @@ +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void +set_bit(uint8_t *bits, unsigned long index) +{ + bits[index / 8] |= (1 << (7 - index % 8)); +} + +void +clear_bit(uint8_t *bits, unsigned long index) +{ + bits[index / 8] &= ~(1 << (7 - index % 8)); +} + +int +has_bit(uint8_t *bits, unsigned long index) +{ + return bits[index / 8] & (1 << (7 - index % 8)); +} + +int +set_nonblocking(int fd) +{ + int oflags; + if ((oflags = fcntl(fd, F_GETFL, 0)) == -1) + return errno; + if (fcntl(fd, F_SETFL, oflags | O_NONBLOCK) == -1) + return errno; + return 0; +} + +int +set_blocking(int fd) +{ + int oflags; + if ((oflags = fcntl(fd, F_GETFL, 0)) == -1) + return errno; + if (fcntl(fd, F_SETFL, oflags & ~O_NONBLOCK) == -1) + return errno; + return 0; +} + +int +mkdirs(char *path) +{ + int err = 0; + char *spos = strchr(path + 1, '/'); // Must ignore the root + + while (spos != NULL) { + *spos = '\0'; + err = mkdir(path, 0777); + *spos = '/'; + + if (err != 0 && errno != EEXIST) { + err = errno; + break; + } + + spos = strchr(spos + 1, '/'); + } + return err; +} + +int +vopen(int *res, int flags, const char *fmt, ...) +{ + int fd, didmkdirs; + char path[PATH_MAX + 1]; + va_list ap; + + va_start(ap, fmt); + if (vsnprintf(path, PATH_MAX, fmt, ap) >= PATH_MAX) { + va_end(ap); + return ENAMETOOLONG; + } + va_end(ap); + + didmkdirs = 0; +again: + fd = open(path, flags, 0666); + if (fd < 0 && errno == ENOENT && (flags & O_CREAT) != 0 && !didmkdirs) { + if (mkdirs(path) == 0) { + didmkdirs = 1; + goto again; + } else + return errno; + } + + if (fd >= 0) { + *res = fd; + return 0; + } else + return errno; +} + +int +canon_path(const char *path, char **res) +{ + char rp[PATH_MAX]; + + if (realpath(path, rp) == NULL) + return errno; +#if 0 + // This could be necessary on solaris. + if (rp[0] != '/') { + char wd[MAXPATHLEN]; + if (getcwd(wd, MAXPATHLEN) == NULL) + return errno; + if (strlcat(wd, "/", MAXPATHLEN) >= MAXPATHLEN) + return ENAMETOOLONG; + if (strlcat(wd, rp, MAXPATHLEN) >= MAXPATHLEN) + return ENAMETOOLONG; + strcpy(rp, wd); + } +#endif + if ((*res = strdup(rp)) == NULL) + return ENOMEM; + + return 0; +} + +size_t +round_to_page(size_t size) +{ + size_t psize = getpagesize(); + size_t rem = size % psize; + if (rem != 0) + size += psize - rem; + return size; +} diff --git a/misc/subr.h b/misc/subr.h new file mode 100644 index 0000000..9467839 --- /dev/null +++ b/misc/subr.h @@ -0,0 +1,21 @@ +#ifndef BTPD_SUBR_H +#define BTPD_SUBR_H + +#define min(x, y) ((x) <= (y) ? (x) : (y)) + +int set_nonblocking(int fd); +int set_blocking(int fd); + +int mkdirs(char *path); + +int vopen(int *resfd, int flags, const char *fmt, ...); + +void set_bit(uint8_t *bits, unsigned long index); +int has_bit(uint8_t *bits, unsigned long index); +void clear_bit(uint8_t *bits, unsigned long index); + +int canon_path(const char *path, char **res); + +size_t round_to_page(size_t size); + +#endif |