about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard Nyberg <rnyberg@murmeldjur.se>2006-01-04 13:38:22 +0000
committerRichard Nyberg <rnyberg@murmeldjur.se>2006-01-04 13:38:22 +0000
commit49e90df57e38514a88692509a65367bde618de9a (patch)
tree0cc0df7cbe8faaf3d614187bdcb9c3f0a7a4898a
parent56320bce47b210f45d0bda8da920568ce16fba47 (diff)
downloadbtpd-49e90df57e38514a88692509a65367bde618de9a.tar.gz
btpd-49e90df57e38514a88692509a65367bde618de9a.zip
o Use the new cm_* content api.
o Unhook cli_if.c from build temporarily. It needs to be fixed.
o Torrent meta data is now kept in subdirectories to $BTPD_HOME/library.
o Added some very incomplete life cycle logic for torrents.

-rw-r--r--btpd/Makefile.am2
-rw-r--r--btpd/btpd.c34
-rw-r--r--btpd/btpd.h2
-rw-r--r--btpd/download.c32
-rw-r--r--btpd/download.h1
-rw-r--r--btpd/download_subr.c95
-rw-r--r--btpd/main.c5
-rw-r--r--btpd/net.c2
-rw-r--r--btpd/net_buf.c9
-rw-r--r--btpd/peer.c23
-rw-r--r--btpd/torrent.c246
-rw-r--r--btpd/torrent.h41
-rw-r--r--btpd/tracker_req.c15
-rw-r--r--btpd/upload.c8
14 files changed, 165 insertions, 350 deletions
diff --git a/btpd/Makefile.am b/btpd/Makefile.am
index 0b4d4c8..0c04f36 100644
--- a/btpd/Makefile.am
+++ b/btpd/Makefile.am
@@ -3,11 +3,11 @@ btpd_SOURCES=\
 	main.c util.c\
 	btpd.c btpd.h\
 	opts.c opts.h\
-	cli_if.c\
 	net.c net.h\
 	net_buf.c net_buf.h\
 	queue.h \
 	peer.c peer.h\
+	content.c content.h\
 	download.c download_subr.c download.h\
 	torrent.c torrent.h\
 	tracker_req.c tracker_req.h\
diff --git a/btpd/btpd.c b/btpd/btpd.c
index 6775595..516602e 100644
--- a/btpd/btpd.c
+++ b/btpd/btpd.c
@@ -52,7 +52,7 @@ btpd_shutdown(void)
     tp = BTPDQ_FIRST(&m_torrents);
     while (tp != NULL) {
         struct torrent *next = BTPDQ_NEXT(tp, entry);
-        torrent_unload(tp);
+        torrent_deactivate(tp);
         tp = next;
     }
     btpd_log(BTPD_L_BTPD, "Exiting.\n");
@@ -136,6 +136,30 @@ btpd_get_peer_id(void)
     return m_peer_id;
 }
 
+static int
+nodot(struct dirent *dp)
+{
+    return !(strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0);
+}
+
+static void
+load_library(void)
+{
+    int ne;
+    struct dirent **entries;
+    if ((ne = scandir("library", &entries, nodot, NULL)) < 0)
+        btpd_err("Couldn't open the library.\n");
+
+    for (int i = 0; i < ne; i++) {
+        struct torrent *tp;
+        struct dirent *e = entries[i];
+        if (torrent_create(&tp, e->d_name) == 0)
+            btpd_add_torrent(tp);
+        free(e);
+    }
+    free(entries);
+}
+
 extern void ipc_init(void);
 
 void
@@ -148,9 +172,15 @@ btpd_init(void)
         m_peer_id[i] = rand_between(0, 255);
 
     net_init();
-    ipc_init();
+    //ipc_init();
     ul_init();
 
+    load_library();
+
+    struct torrent *tp;
+    BTPDQ_FOREACH(tp, &m_torrents, entry)
+        torrent_activate(tp);
+
     signal(SIGPIPE, SIG_IGN);
 
     signal_set(&m_sigint, SIGINT, signal_cb, NULL);
diff --git a/btpd/btpd.h b/btpd/btpd.h
index 06d5925..68aaa0b 100644
--- a/btpd/btpd.h
+++ b/btpd/btpd.h
@@ -25,7 +25,7 @@
 #include "download.h"
 #include "upload.h"
 #include "subr.h"
-
+#include "content.h"
 #include "opts.h"
 
 #define BTPD_VERSION (PACKAGE_NAME "/" PACKAGE_VERSION)
diff --git a/btpd/download.c b/btpd/download.c
index fd7f680..c2ff7f6 100644
--- a/btpd/download.c
+++ b/btpd/download.c
@@ -1,5 +1,4 @@
-#include <sys/types.h>
-#include <sys/mman.h>
+#include <math.h>
 
 #include "btpd.h"
 #include "tracker_req.h"
@@ -7,6 +6,10 @@
 void
 dl_start(struct torrent *tp)
 {
+    BTPDQ_INIT(&tp->getlst);
+    tp->busy_field = btpd_calloc((size_t)ceil(tp->meta.npieces / 8.0), 1);
+    tp->piece_count = btpd_calloc(tp->meta.npieces,
+        sizeof(*(tp->piece_count)));
 }
 
 void
@@ -15,6 +18,8 @@ dl_stop(struct torrent *tp)
     struct piece *pc;
     while ((pc = BTPDQ_FIRST(&tp->getlst)) != NULL)
         piece_free(pc);
+    free(tp->busy_field);
+    free(tp->piece_count);
 }
 
 /*
@@ -28,7 +33,7 @@ dl_on_piece_ann(struct peer *p, uint32_t index)
 {
     struct torrent *tp = p->tp;
     tp->piece_count[index]++;
-    if (has_bit(tp->piece_field, index))
+    if (cm_has_piece(tp, index))
         return;
     struct piece *pc = dl_find_piece(tp, index);
     if (tp->endgame) {
@@ -98,10 +103,6 @@ dl_on_ok_piece(struct piece *pc)
 
     btpd_log(BTPD_L_POL, "Got piece: %u.\n", pc->index);
 
-    set_bit(tp->piece_field, pc->index);
-    tp->have_npieces++;
-    msync(tp->imem, tp->isiz, MS_ASYNC);
-
     struct net_buf *have = nb_create_have(pc->index);
     BTPDQ_FOREACH(p, &tp->peers, p_entry)
         peer_send(p, have);
@@ -114,7 +115,7 @@ dl_on_ok_piece(struct piece *pc)
     assert(pc->nreqs == 0);
     piece_free(pc);
 
-    if (torrent_has_all(tp)) {
+    if (cm_full(tp)) {
         btpd_log(BTPD_L_BTPD, "Finished: %s.\n", tp->relpath);
         tracker_req(tp, TR_COMPLETED);
         BTPDQ_FOREACH(p, &tp->peers, p_entry)
@@ -133,13 +134,11 @@ dl_on_bad_piece(struct piece *pc)
     btpd_log(BTPD_L_ERROR, "Bad hash for piece %u of %s.\n",
         pc->index, tp->relpath);
 
-    for (uint32_t i = 0; i < pc->nblocks; i++) {
+    for (uint32_t i = 0; i < pc->nblocks; i++)
         clear_bit(pc->down_field, i);
-        clear_bit(pc->have_field, i);
-    }
+
     pc->ngot = 0;
     pc->nbusy = 0;
-    msync(tp->imem, tp->isiz, MS_ASYNC);
 
     if (tp->endgame) {
         struct peer *p;
@@ -177,10 +176,7 @@ dl_on_block(struct peer *p, struct block_request *req,
     struct block *blk = req->blk;
     struct piece *pc = blk->pc;
 
-    off_t cbegin = index * p->tp->meta.piece_length + begin;
-    torrent_put_bytes(p->tp, data, cbegin, length);
-
-    set_bit(pc->have_field, begin / PIECE_BLOCKLEN);
+    cm_put_block(p->tp, index, begin / PIECE_BLOCKLEN, data);
     pc->ngot++;
 
     if (tp->endgame) {
@@ -204,7 +200,7 @@ dl_on_block(struct peer *p, struct block_request *req,
         }
         BTPDQ_INIT(&blk->reqs);
         if (pc->ngot == pc->nblocks)
-            dl_on_piece(pc);
+            cm_test_piece(pc);
     } else {
         BTPDQ_REMOVE(&blk->reqs, req, blk_entry);
         free(req);
@@ -213,7 +209,7 @@ dl_on_block(struct peer *p, struct block_request *req,
         clear_bit(pc->down_field, begin / PIECE_BLOCKLEN);
         pc->nbusy--;
         if (pc->ngot == pc->nblocks)
-            dl_on_piece(pc);
+            cm_test_piece(pc);
         if (peer_leech_ok(p) && !peer_laden(p))
             dl_assign_requests(p);
     }
diff --git a/btpd/download.h b/btpd/download.h
index a4ae402..94f71da 100644
--- a/btpd/download.h
+++ b/btpd/download.h
@@ -7,7 +7,6 @@ int piece_full(struct piece *pc);
 void piece_free(struct piece *pc);
 
 void dl_on_piece_unfull(struct piece *pc);
-void dl_on_piece(struct piece *pc);
 
 struct piece *dl_new_piece(struct torrent *tp, uint32_t index);
 struct piece *dl_find_piece(struct torrent *tp, uint32_t index);
diff --git a/btpd/download_subr.c b/btpd/download_subr.c
index f77d7db..f1db9a7 100644
--- a/btpd/download_subr.c
+++ b/btpd/download_subr.c
@@ -47,9 +47,7 @@ piece_alloc(struct torrent *tp, uint32_t index)
     pc = btpd_calloc(1, mem);
     pc->tp = tp;
     pc->down_field = (uint8_t *)(pc + 1);
-    pc->have_field =
-        tp->block_field +
-        index * (size_t)ceil(tp->meta.piece_length / (double)(1 << 17));
+    pc->have_field = cm_get_block_field(tp, index);
 
     pc->index = index;
     pc->nblocks = nblocks;
@@ -60,6 +58,7 @@ piece_alloc(struct torrent *tp, uint32_t index)
     for (unsigned i = 0; i < nblocks; i++)
         if (has_bit(pc->have_field, i))
             pc->ngot++;
+    assert(pc->ngot < pc->nblocks);
 
     pc->blocks = (struct block *)(pc->down_field + field);
     for (unsigned i = 0; i < nblocks; i++) {
@@ -108,7 +107,7 @@ static int
 dl_should_enter_endgame(struct torrent *tp)
 {
     int should;
-    if (tp->have_npieces + tp->npcs_busy == tp->meta.npieces) {
+    if (cm_get_npieces(tp) + tp->npcs_busy == tp->meta.npieces) {
         should = 1;
         struct piece *pc;
         BTPDQ_FOREACH(pc, &tp->getlst, entry) {
@@ -196,78 +195,9 @@ dl_find_piece(struct torrent *tp, uint32_t index)
 }
 
 static int
-test_hash(struct torrent *tp, uint8_t *hash, unsigned long index)
-{
-    if (tp->meta.piece_hash != NULL)
-        return memcmp(hash, tp->meta.piece_hash[index], SHA_DIGEST_LENGTH);
-    else {
-        char piece_hash[SHA_DIGEST_LENGTH];
-        int fd;
-        int bufi;
-        int err;
-
-        err = vopen(&fd, O_RDONLY, "%s/torrent", tp->relpath);
-        if (err != 0)
-            btpd_err("test_hash: %s\n", strerror(err));
-
-        err = lseek(fd, tp->meta.pieces_off + index * SHA_DIGEST_LENGTH,
-            SEEK_SET);
-        if (err < 0)
-            btpd_err("test_hash: %s\n", strerror(errno));
-
-        bufi = 0;
-        while (bufi < SHA_DIGEST_LENGTH) {
-            ssize_t nread =
-                read(fd, piece_hash + bufi, SHA_DIGEST_LENGTH - bufi);
-            bufi += nread;
-        }
-        close(fd);
-
-        return memcmp(hash, piece_hash, SHA_DIGEST_LENGTH);
-    }
-}
-
-static int
-ro_fd_cb(const char *path, int *fd, void *arg)
-{
-    struct torrent *tp = arg;
-    return vopen(fd, O_RDONLY, "%s/content/%s", tp->relpath, path);
-}
-
-static void
-torrent_test_piece(struct piece *pc)
-{
-    struct torrent *tp = pc->tp;
-    int err;
-    uint8_t hash[20];
-    struct bt_stream_ro *bts;
-    off_t plen = torrent_piece_size(tp, pc->index);
-
-    if ((bts = bts_open_ro(&tp->meta, pc->index * tp->meta.piece_length,
-             ro_fd_cb, tp)) == NULL)
-        btpd_err("Out of memory.\n");
-
-    if ((err = bts_sha(bts, plen, hash)) != 0)
-        btpd_err("Ouch! %s\n", strerror(err));
-
-    bts_close_ro(bts);
-
-    if (test_hash(tp, hash, pc->index) == 0)
-        dl_on_ok_piece(pc);
-    else
-        dl_on_bad_piece(pc);
-}
-
-void
-dl_on_piece(struct piece *pc)
-{
-    torrent_test_piece(pc);
-}
-
-static int
 dl_piece_startable(struct peer *p, uint32_t index)
 {
-    return peer_has(p, index) && !has_bit(p->tp->piece_field, index)
+    return peer_has(p, index) && !cm_has_piece(p->tp, index)
         && !has_bit(p->tp->busy_field, index);
 }
 
@@ -319,10 +249,9 @@ dl_choose_rarest(struct peer *p, uint32_t *res)
 }
 
 /*
- * Called from either dl_piece_assign_requests or dl_new_piece,
- * when a pice becomes full. The wanted level of the peers
- * that has this piece will be decreased. This function is
- * the only one that may trigger end game.
+ * Called from dl_piece_assign_requests when a piece becomes full.
+ * The wanted level of the peers that has this piece will be decreased.
+ * This function is the only one that may trigger end game.
  */
 static void
 dl_on_piece_full(struct piece *pc)
@@ -349,15 +278,7 @@ struct piece *
 dl_new_piece(struct torrent *tp, uint32_t index)
 {
     btpd_log(BTPD_L_POL, "Started on piece %u.\n", index);
-    struct piece *pc = piece_alloc(tp, index);
-    if (pc->ngot == pc->nblocks) {
-        dl_on_piece_full(pc);
-        dl_on_piece(pc);
-        if (dl_should_enter_endgame(tp))
-            dl_enter_endgame(tp);
-        return NULL;
-    } else
-        return pc;
+    return piece_alloc(tp, index);
 }
 
 /*
diff --git a/btpd/main.c b/btpd/main.c
index c41d6b2..fe5f9ba 100644
--- a/btpd/main.c
+++ b/btpd/main.c
@@ -57,6 +57,9 @@ setup_daemon(const char *dir)
     if (chdir(dir) != 0)
         err(1, "Couldn't change working directory to '%s'", dir);
 
+    if (mkdir("library", 0777) == -1 && errno != EEXIST)
+        err(1, "Couldn't create library");
+
     pidfd = open("pid", O_CREAT|O_WRONLY|O_NONBLOCK|O_EXLOCK, 0666);
     if (pidfd == -1)
         err(1, "Couldn't open 'pid'");
@@ -170,9 +173,9 @@ args_done:
     event_init();
 
     btpd_init();
-    torrent_load("test");
 
     event_dispatch();
+
     btpd_err("Unexpected exit from libevent.\n");
 
     return 1;
diff --git a/btpd/net.c b/btpd/net.c
index cf42df5..8b7ebfc 100644
--- a/btpd/net.c
+++ b/btpd/net.c
@@ -208,7 +208,7 @@ net_dispatch_msg(struct peer *p, const char *buf)
             length = net_read32(buf + 8);
             if ((length > PIECE_BLOCKLEN
                     || index >= p->tp->meta.npieces
-                    || !has_bit(p->tp->piece_field, index)
+                    || cm_has_piece(p->tp, index)
                     || begin + length > torrent_piece_size(p->tp, index))) {
                 btpd_log(BTPD_L_MSG, "bad request: (%u, %u, %u) from %p\n",
                          index, begin, length, p);
diff --git a/btpd/net_buf.c b/btpd/net_buf.c
index 2402e01..880d94f 100644
--- a/btpd/net_buf.c
+++ b/btpd/net_buf.c
@@ -121,9 +121,10 @@ nb_create_have(uint32_t index)
 struct net_buf *
 nb_create_multihave(struct torrent *tp)
 {
-    struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * tp->have_npieces);
-    for (uint32_t i = 0, count = 0; count < tp->have_npieces; i++) {
-        if (has_bit(tp->piece_field, i)) {
+    uint32_t have_npieces = cm_get_npieces(tp);
+    struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * have_npieces);
+    for (uint32_t i = 0, count = 0; count < have_npieces; i++) {
+        if (cm_has_piece(tp, i)) {
             net_write32(out->buf + count * 9, 5);
             out->buf[count * 9 + 4] = MSG_HAVE;
             net_write32(out->buf + count * 9 + 5, i);
@@ -183,7 +184,7 @@ nb_create_bitdata(struct torrent *tp)
 {
     uint32_t plen = ceil(tp->meta.npieces / 8.0);
     struct net_buf *out =
-        nb_create_set(NB_BITDATA, tp->piece_field, plen, kill_buf_no);
+        nb_create_set(NB_BITDATA, cm_get_piece_field(tp), plen, kill_buf_no);
     return out;
 }
 
diff --git a/btpd/peer.c b/btpd/peer.c
index c7273ff..3367512 100644
--- a/btpd/peer.c
+++ b/btpd/peer.c
@@ -336,8 +336,8 @@ peer_on_shake(struct peer *p)
     printid[i] = '\0';
     btpd_log(BTPD_L_MSG, "received shake(%s) from %p\n", printid, p);
     p->piece_field = btpd_calloc(1, (int)ceil(p->tp->meta.npieces / 8.0));
-    if (p->tp->have_npieces > 0) {
-        if (p->tp->have_npieces * 9 < 5 + ceil(p->tp->meta.npieces / 8.0))
+    if (cm_get_npieces(p->tp) > 0) {
+        if (cm_get_npieces(p->tp) * 9 < 5 + ceil(p->tp->meta.npieces / 8.0))
             peer_send(p, nb_create_multihave(p->tp));
         else {
             peer_send(p, nb_create_bitfield(p->tp));
@@ -467,15 +467,16 @@ peer_on_request(struct peer *p, uint32_t index, uint32_t begin,
     btpd_log(BTPD_L_MSG, "received request(%u,%u,%u) from %p\n",
         index, begin, length, p);
     if ((p->flags & PF_NO_REQUESTS) == 0) {
-        off_t cbegin = index * p->tp->meta.piece_length + begin;
-        char * content = torrent_get_bytes(p->tp, cbegin, length);
-        peer_send(p, nb_create_piece(index, begin, length));
-        peer_send(p, nb_create_torrentdata(content, length));
-        p->npiece_msgs++;
-        if (p->npiece_msgs >= MAXPIECEMSGS) {
-            peer_send(p, nb_create_choke());
-            peer_send(p, nb_create_unchoke());
-            p->flags |= PF_NO_REQUESTS;
+        char *content;
+        if (cm_get_bytes(p->tp, index, begin, length, &content) == 0) {
+            peer_send(p, nb_create_piece(index, begin, length));
+            peer_send(p, nb_create_torrentdata(content, length));
+            p->npiece_msgs++;
+            if (p->npiece_msgs >= MAXPIECEMSGS) {
+                peer_send(p, nb_create_choke());
+                peer_send(p, nb_create_unchoke());
+                p->flags |= PF_NO_REQUESTS;
+            }
         }
     }
 }
diff --git a/btpd/torrent.c b/btpd/torrent.c
index 850e229..f317e67 100644
--- a/btpd/torrent.c
+++ b/btpd/torrent.c
@@ -19,194 +19,6 @@
 #include "tracker_req.h"
 #include "stream.h"
 
-static int
-ro_fd_cb(const char *path, int *fd, void *arg)
-{
-    struct torrent *tp = arg;
-    return vopen(fd, O_RDONLY, "%s/content/%s", tp->relpath, path);
-}
-
-static int
-wo_fd_cb(const char *path, int *fd, void *arg)
-{
-    struct torrent *tp = arg;
-    return vopen(fd, O_WRONLY|O_CREAT, "%s/content/%s", tp->relpath, path);
-}
-
-static int
-torrent_load3(const char *file, struct metainfo *mi, char *mem, size_t memsiz)
-{
-    struct torrent *tp = btpd_calloc(1, sizeof(*tp));
-
-    tp->relpath = strdup(file);
-    if (tp->relpath == NULL)
-        btpd_err("Out of memory.\n");
-
-    tp->piece_count = btpd_calloc(mi->npieces, sizeof(tp->piece_count[0]));
-    tp->busy_field = btpd_calloc(ceil(mi->npieces / 8.0), 1);
-
-    BTPDQ_INIT(&tp->peers);
-    BTPDQ_INIT(&tp->getlst);
-
-    tp->imem = mem;
-    tp->isiz = memsiz;
-
-    tp->piece_field = tp->imem;
-    tp->block_field =
-        (uint8_t *)tp->imem + (size_t)ceil(mi->npieces / 8.0);
-
-    for (uint32_t i = 0; i < mi->npieces; i++)
-        if (has_bit(tp->piece_field, i))
-            tp->have_npieces++;
-
-    tp->meta = *mi;
-    free(mi);
-
-    btpd_add_torrent(tp);
-    net_add_torrent(tp);
-
-    tracker_req(tp, TR_STARTED);
-
-    return 0;
-}
-
-static int
-torrent_load2(const char *name, struct metainfo *mi)
-{
-    int error, ifd;
-    struct stat sb;
-    char *mem;
-    size_t memsiz;
-    const char *file = name;
-
-    if ((error = vopen(&ifd, O_RDWR, "%s/resume", file)) != 0) {
-        btpd_log(BTPD_L_ERROR, "Error opening %s.i: %s.\n",
-            file, strerror(error));
-        return error;
-    }
-
-    if (fstat(ifd, &sb) == -1) {
-        error = errno;
-        btpd_log(BTPD_L_ERROR, "Error stating %s.i: %s.\n",
-            file, strerror(error));
-        close(ifd);
-        return error;
-    }
-
-    memsiz =
-        ceil(mi->npieces / 8.0) +
-        mi->npieces * ceil(mi->piece_length / (double)(1 << 17));
-
-    if (sb.st_size != memsiz) {
-        btpd_log(BTPD_L_ERROR, "File has wrong size: %s.i.\n", file);
-        close(ifd);
-        return EINVAL;
-    }
-
-    mem = mmap(NULL, memsiz, PROT_READ|PROT_WRITE, MAP_SHARED, ifd, 0);
-    if (mem == MAP_FAILED)
-        btpd_err("Error mmap'ing %s.i: %s.\n", file, strerror(errno));
-
-    close(ifd);
-
-    if ((error = torrent_load3(file, mi, mem, memsiz) != 0)) {
-        munmap(mem, memsiz);
-        return error;
-    }
-
-    return 0;
-}
-
-int
-torrent_load(const char *name)
-{
-    struct metainfo *mi;
-    int error;
-    char file[PATH_MAX];
-    snprintf(file, PATH_MAX, "%s/torrent", name);
-
-    if ((error = load_metainfo(file, -1, 0, &mi)) != 0) {
-        btpd_log(BTPD_L_ERROR, "Couldn't load metainfo file %s: %s.\n",
-            file, strerror(error));
-        return error;
-    }
-
-    if (btpd_get_torrent(mi->info_hash) != NULL) {
-        btpd_log(BTPD_L_BTPD, "%s has same hash as an already loaded torrent.\n", file);
-        error = EEXIST;
-    }
-
-    if (error == 0)
-        error = torrent_load2(name, mi);
-
-    if (error != 0) {
-        clear_metainfo(mi);
-        free(mi);
-    }
-
-    return error;
-}
-
-void
-torrent_unload(struct torrent *tp)
-{
-    btpd_log(BTPD_L_BTPD, "Unloading %s.\n", tp->relpath);
-
-    net_del_torrent(tp);
-
-    tracker_req(tp, TR_STOPPED);
-
-    free(tp->piece_count);
-    free(tp->busy_field);
-    free((void *)tp->relpath);
-    clear_metainfo(&tp->meta);
-
-    munmap(tp->imem, tp->isiz);
-
-    btpd_del_torrent(tp);
-    free(tp);
-}
-
-off_t
-torrent_bytes_left(struct torrent *tp)
-{
-    if (tp->have_npieces == 0)
-        return tp->meta.total_length;
-    else if (has_bit(tp->piece_field, tp->meta.npieces - 1)) {
-        return tp->meta.total_length -
-            ((tp->have_npieces - 1) * tp->meta.piece_length +
-            tp->meta.total_length % tp->meta.piece_length);
-    } else
-        return tp->meta.total_length -
-            tp->have_npieces * tp->meta.piece_length;
-}
-
-char *
-torrent_get_bytes(struct torrent *tp, off_t start, size_t len)
-{
-    char *buf = btpd_malloc(len);
-    struct bt_stream_ro *bts;
-    if ((bts = bts_open_ro(&tp->meta, start, ro_fd_cb, tp)) == NULL)
-        btpd_err("Out of memory.\n");
-    if (bts_read_ro(bts, buf, len) != 0)
-        btpd_err("Io error.\n");
-    bts_close_ro(bts);
-    return buf;
-}
-
-void
-torrent_put_bytes(struct torrent *tp, const char *buf, off_t start, size_t len)
-{
-    int err;
-    struct bt_stream_wo *bts;
-    if ((bts = bts_open_wo(&tp->meta, start, wo_fd_cb, tp)) == NULL)
-        btpd_err("Out of memory.\n");
-    if ((err = bts_write_wo(bts, buf, len)) != 0)
-        btpd_err("Io error1: %s\n", strerror(err));
-    if ((err = bts_close_wo(bts)) != 0)
-        btpd_err("Io error2: %s\n", strerror(err));
-}
-
 int
 torrent_has_peer(struct torrent *tp, const uint8_t *id)
 {
@@ -244,8 +56,62 @@ torrent_block_size(struct piece *pc, uint32_t index)
     }
 }
 
+void
+torrent_activate(struct torrent *tp)
+{
+    assert(tp->state == T_INACTIVE);
+    tp->state = T_STARTING;
+    cm_start(tp);
+}
+
+void
+torrent_deactivate(struct torrent *tp)
+{
+
+}
+
 int
-torrent_has_all(struct torrent *tp)
+torrent_create(struct torrent **res, const char *path)
 {
-    return tp->have_npieces == tp->meta.npieces;
+    struct metainfo *mi;
+    int error;
+    char file[PATH_MAX];
+    snprintf(file, PATH_MAX, "library/%s/torrent", path);
+
+    if ((error = load_metainfo(file, -1, 0, &mi)) != 0) {
+        btpd_log(BTPD_L_ERROR, "Couldn't load metainfo file %s: %s.\n",
+            file, strerror(error));
+        return error;
+    }
+
+    if (btpd_get_torrent(mi->info_hash) != NULL) {
+        btpd_log(BTPD_L_BTPD,
+            "%s has same hash as an already loaded torrent.\n", path);
+        error = EEXIST;
+    }
+
+    if (error == 0) {
+        *res = btpd_calloc(1, sizeof(**res));
+        (*res)->relpath = strdup(path);
+        (*res)->meta = *mi;
+        free(mi);
+    } else {
+        clear_metainfo(mi);
+        free(mi);
+    }
+
+    return error;
+}
+
+void torrent_cm_cb(struct torrent *tp, enum cm_state state)
+{
+    switch (state) {
+    case CM_STARTED:
+        net_add_torrent(tp);
+        tracker_req(tp, TR_STARTED);
+    case CM_STOPPED:
+        abort();
+    case CM_ERROR:
+        abort();
+    }
 }
diff --git a/btpd/torrent.h b/btpd/torrent.h
index cb3be8e..1797a73 100644
--- a/btpd/torrent.h
+++ b/btpd/torrent.h
@@ -23,7 +23,7 @@ struct piece {
 
     struct block *blocks;
 
-    uint8_t *have_field;
+    const uint8_t *have_field;
     uint8_t *down_field;
 
     BTPDQ_ENTRY(piece) entry;
@@ -31,26 +31,29 @@ struct piece {
 
 BTPDQ_HEAD(piece_tq, piece);
 
+enum torrent_state {
+    T_INACTIVE,
+    T_STARTING,
+    T_ACTIVE,
+    T_STOPPING
+};
+
 struct torrent {
     const char *relpath;
     struct metainfo meta;
 
+    enum torrent_state state;
+
+    struct content *cp;
+
     BTPDQ_ENTRY(torrent) entry;
     BTPDQ_ENTRY(torrent) net_entry;
 
-    void *imem;
-    size_t isiz;
-
     int net_active;
 
-    uint8_t *piece_field;
-    uint8_t *block_field;
-
     uint8_t *busy_field;
     uint32_t npcs_busy;
 
-    uint32_t have_npieces;
-
     unsigned *piece_count;
 
     uint64_t uploaded, downloaded;
@@ -66,21 +69,21 @@ struct torrent {
 
 BTPDQ_HEAD(torrent_tq, torrent);
 
-off_t torrent_bytes_left(struct torrent *tp);
-
-char *torrent_get_bytes(struct torrent *tp, off_t start, size_t len);
-void torrent_put_bytes(struct torrent *tp, const char *buf,
-                       off_t start, size_t len);
-
-int torrent_load(const char *metafile);
-
-void torrent_unload(struct torrent *tp);
+int torrent_create(struct torrent **res, const char *path);
+void torrent_activate(struct torrent *tp);
+void torrent_deactivate(struct torrent *tp);
 
 int torrent_has_peer(struct torrent *tp, const uint8_t *id);
 
 off_t torrent_piece_size(struct torrent *tp, uint32_t index);
 uint32_t torrent_block_size(struct piece *pc, uint32_t index);
 
-int torrent_has_all(struct torrent *tp);
+enum cm_state {
+    CM_STARTED,
+    CM_STOPPED,
+    CM_ERROR
+};
+
+void torrent_cm_cb(struct torrent *tp, enum cm_state state);
 
 #endif
diff --git a/btpd/tracker_req.c b/btpd/tracker_req.c
index 55d5669..e059b04 100644
--- a/btpd/tracker_req.c
+++ b/btpd/tracker_req.c
@@ -128,14 +128,9 @@ tracker_done(pid_t pid, void *arg)
     }
 
 out:
-    if (failed) {
-        if (req->tr_event == TR_STARTED) {
-            btpd_log(BTPD_L_BTPD,
-                "Start request failed for %s.\n", tp->relpath);
-            torrent_unload(tp);
-        } else
-            ;//tp->tracker_time = btpd_seconds + 10;
-    }
+    if (failed)
+        ;//tp->tracker_time = btpd_seconds + 10;
+
     munmap(req->res, REQ_SIZE);
     free(req);
 }
@@ -165,7 +160,7 @@ create_url(struct tracker_req *req, struct torrent *tp, char **url)
     const uint8_t *peer_id = btpd_get_peer_id();
     char qc;
     int i;
-    uint64_t left;
+    off_t left;
     const char *event;
 
     event = event2str(req->tr_event);
@@ -178,7 +173,7 @@ create_url(struct tracker_req *req, struct torrent *tp, char **url)
     for (i = 0; i < 20; i++)
         snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[i]);
 
-    left = torrent_bytes_left(tp);
+    left = cm_bytes_left(tp);
 
     i = asprintf(url, "%s%cinfo_hash=%s"
                  "&peer_id=%s"
diff --git a/btpd/upload.c b/btpd/upload.c
index 5ae7f1b..5e5174d 100644
--- a/btpd/upload.c
+++ b/btpd/upload.c
@@ -19,8 +19,8 @@ rate_cmp(const void *arg1, const void *arg2)
 {
     struct peer *p1 = (*(struct peer_sort **)arg1)->p;
     struct peer *p2 = (*(struct peer_sort **)arg2)->p;
-    unsigned long rate1 = torrent_has_all(p1->tp) ? p1->rate_up : p1->rate_dwn;
-    unsigned long rate2 = torrent_has_all(p2->tp) ? p2->rate_up : p2->rate_dwn;
+    unsigned long rate1 = cm_full(p1->tp) ? p1->rate_up : p1->rate_dwn;
+    unsigned long rate2 = cm_full(p2->tp) ? p2->rate_up : p2->rate_dwn;
     if (rate1 < rate2)
         return -1;
     else if (rate1 == rate2)
@@ -51,8 +51,8 @@ choke_do(void)
         int unchoked[m_npeers];
 
         BTPDQ_FOREACH(p, &m_peerq, ul_entry) {
-            if (((torrent_has_all(p->tp) && p->rate_up > 0)
-                    || (!torrent_has_all(p->tp) && p->rate_dwn > 0))) {
+            if (((cm_full(p->tp) && p->rate_up > 0)
+                    || (!cm_full(p->tp) && p->rate_dwn > 0))) {
                 worthy[nworthy].p = p;
                 worthy[nworthy].i = i;
                 nworthy++;