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/stream.c | |
| 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/stream.c')
| -rw-r--r-- | misc/stream.c | 241 |
1 files changed, 241 insertions, 0 deletions
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; +} |