about summary refs log tree commit diff
path: root/misc/stream.c
diff options
context:
space:
mode:
authorRichard Nyberg <rnyberg@murmeldjur.se>2005-06-24 09:51:38 +0000
committerRichard Nyberg <rnyberg@murmeldjur.se>2005-06-24 09:51:38 +0000
commitdd0d462afae75ff243f8cd1528963f9ad489706d (patch)
tree2ef874a1fe5212245814d16f4c9b389524aed9d1 /misc/stream.c
downloadbtpd-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.c241
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;
+}