summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--btpd/content.c59
-rw-r--r--btpd/net.c38
-rw-r--r--btpd/net.h3
-rw-r--r--btpd/net_buf.c44
-rw-r--r--btpd/tlib.c161
-rw-r--r--btpd/tlib.h17
-rw-r--r--btpd/torrent.c35
-rw-r--r--misc/subr.c42
-rw-r--r--misc/subr.h5
9 files changed, 230 insertions, 174 deletions
diff --git a/btpd/content.c b/btpd/content.c
index 1879b3f..5807477 100644
--- a/btpd/content.c
+++ b/btpd/content.c
@@ -30,7 +30,7 @@ struct content {
     struct bt_stream *rds;
     struct bt_stream *wrs;
 
-    struct event save_timer;
+    struct resume_data *resd;
 };
 
 #define ZEROBUFLEN (1 << 14)
@@ -103,8 +103,7 @@ void
 cm_kill(struct torrent *tp)
 {
     struct content *cm = tp->cm;
-    free(cm->piece_field);
-    free(cm->block_field);
+    tlib_close_resume(cm->resd);
     free(cm->pos_field);
     free(cm);
     tp->cm = NULL;
@@ -117,9 +116,8 @@ cm_save(struct torrent *tp)
 {
     struct file_time_size fts[tp->nfiles];
     stat_and_adjust(tp, fts);
-    tlib_save_resume(tp->tl, tp->nfiles, fts,
-        ceil(tp->npieces / 8.0), tp->cm->piece_field,
-        tp->cm->bppbf * tp->npieces, tp->cm->block_field);
+    for (int i = 0; i < tp->nfiles; i++)
+        resume_set_fts(tp->cm->resd, i, fts + i);
 }
 
 static void
@@ -134,18 +132,18 @@ cm_on_error(struct torrent *tp)
 static void
 cm_write_done(struct torrent *tp)
 {
-    int err = 0;
+    int err;
     struct content *cm = tp->cm;
 
-    if ((err = bts_close(cm->wrs)) != 0)
+    err = bts_close(cm->wrs);
+    cm->wrs = NULL;
+    if (err && !cm->error) {
         btpd_log(BTPD_L_ERROR, "error closing write stream for '%s' (%s).\n",
             torrent_name(tp), strerror(err));
-    cm->wrs = NULL;
-    btpd_ev_del(&cm->save_timer);
-    if (!err)
-        cm_save(tp);
-    else
         cm_on_error(tp);
+    }
+    if (!cm->error)
+        cm_save(tp);
 }
 
 void
@@ -195,27 +193,17 @@ cm_started(struct torrent *tp)
     return cm->state == CM_ACTIVE;
 }
 
-#define SAVE_INTERVAL (& (struct timeval) { 15, 0 })
-
-static void
-save_timer_cb(int fd, short type, void *arg)
-{
-    struct torrent *tp = arg;
-    btpd_ev_add(&tp->cm->save_timer, SAVE_INTERVAL);
-    cm_save(tp);
-}
-
 void
 cm_create(struct torrent *tp, const char *mi)
 {
     size_t pfield_size = ceil(tp->npieces / 8.0);
     struct content *cm = btpd_calloc(1, sizeof(*cm));
     cm->bppbf = ceil((double)tp->piece_length / (1 << 17));
-    cm->piece_field = btpd_calloc(pfield_size, 1);
     cm->pos_field = btpd_calloc(pfield_size, 1);
-    cm->block_field = btpd_calloc(tp->npieces * cm->bppbf, 1);
-
-    evtimer_set(&cm->save_timer, save_timer_cb, tp);
+    cm->resd = tlib_open_resume(tp->tl, tp->nfiles, pfield_size,
+        cm->bppbf * tp->npieces);
+    cm->piece_field = resume_piece_field(cm->resd);
+    cm->block_field = resume_block_field(cm->resd);
 
     tp->cm = cm;
 }
@@ -431,9 +419,8 @@ startup_test_end(struct torrent *tp, int unclean)
     if (unclean) {
         struct start_test_data *std = BTPDQ_FIRST(&m_startq);
         BTPDQ_REMOVE(&m_startq, std, entry);
-        tlib_save_resume(tp->tl, tp->nfiles, std->fts,
-            ceil(tp->npieces / 8.0), cm->piece_field, cm->bppbf * 8,
-            cm->block_field);
+        for (int i = 0; i < tp->nfiles; i++)
+            resume_set_fts(cm->resd, i, std->fts + i);
         free(std->fts);
         free(std);
     }
@@ -447,7 +434,6 @@ startup_test_end(struct torrent *tp, int unclean)
             cm_on_error(tp);
             return;
         }
-        btpd_ev_add(&cm->save_timer, SAVE_INTERVAL);
     }
     cm->state = CM_ACTIVE;
 }
@@ -520,7 +506,7 @@ cm_start(struct torrent *tp, int force_test)
         return;
     }
 
-    fts = btpd_calloc(tp->nfiles * 2, sizeof(*fts));
+    fts = btpd_calloc(tp->nfiles, sizeof(*fts));
 
     if ((err = stat_and_adjust(tp, fts)) != 0) {
         free(fts);
@@ -528,13 +514,10 @@ cm_start(struct torrent *tp, int force_test)
         return;
     }
 
-    if (tlib_load_resume(tp->tl, tp->nfiles, fts + tp->nfiles,
-            ceil(tp->npieces / 8.0), cm->piece_field,
-            cm->bppbf * tp->npieces, cm->block_field) != 0)
-        run_test = 1;
     for (int i = 0; i < tp->nfiles; i++) {
-        if ((fts[i].mtime != fts[i + tp->nfiles].mtime ||
-                fts[i].size != fts[i + tp->nfiles].size)) {
+        struct file_time_size rfts;
+        resume_get_fts(cm->resd, i, &rfts);
+        if ((fts[i].mtime != rfts.mtime || fts[i].size != rfts.size)) {
             run_test = 1;
             break;
         }
diff --git a/btpd/net.c b/btpd/net.c
index 5551221..f0eb39e 100644
--- a/btpd/net.c
+++ b/btpd/net.c
@@ -116,24 +116,6 @@ net_active(struct torrent *tp)
     return tp->net->active;
 }
 
-void
-net_write32(void *buf, uint32_t num)
-{
-    uint8_t *p = buf;
-    *p = (num >> 24) & 0xff;
-    *(p + 1) = (num >> 16) & 0xff;
-    *(p + 2) = (num >> 8) & 0xff;
-    *(p + 3) = num & 0xff;
-}
-
-uint32_t
-net_read32(const void *buf)
-{
-    const uint8_t *p = buf;
-    return (uint32_t)*p << 24 | (uint32_t)*(p + 1) << 16
-        | (uint16_t)*(p + 2) << 8 | *(p + 3);
-}
-
 #define BLOCK_MEM_COUNT 4
 
 static unsigned long
@@ -256,7 +238,7 @@ net_dispatch_msg(struct peer *p, const char *buf)
         peer_on_uninterest(p);
         break;
     case MSG_HAVE:
-        peer_on_have(p, net_read32(buf));
+        peer_on_have(p, dec_be32(buf));
         break;
     case MSG_BITFIELD:
         if (p->npieces == 0)
@@ -266,9 +248,9 @@ net_dispatch_msg(struct peer *p, const char *buf)
         break;
     case MSG_REQUEST:
         if ((p->flags & (PF_P_WANT|PF_I_CHOKE)) == PF_P_WANT) {
-            index = net_read32(buf);
-            begin = net_read32(buf + 4);
-            length = net_read32(buf + 8);
+            index = dec_be32(buf);
+            begin = dec_be32(buf + 4);
+            length = dec_be32(buf + 8);
             if ((length > PIECE_BLOCKLEN
                     || index >= p->n->tp->npieces
                     || !cm_has_piece(p->n->tp, index)
@@ -282,9 +264,9 @@ net_dispatch_msg(struct peer *p, const char *buf)
         }
         break;
     case MSG_CANCEL:
-        index = net_read32(buf);
-        begin = net_read32(buf + 4);
-        length = net_read32(buf + 8);
+        index = dec_be32(buf);
+        begin = dec_be32(buf + 4);
+        length = dec_be32(buf + 8);
         peer_on_cancel(p, index, begin, length);
         break;
     case MSG_PIECE:
@@ -359,7 +341,7 @@ net_state(struct peer *p, const char *buf)
         peer_set_in_state(p, BTP_MSGSIZE, 4);
         break;
     case BTP_MSGSIZE:
-        p->in.msg_len = net_read32(buf);
+        p->in.msg_len = dec_be32(buf);
         if (p->in.msg_len == 0)
             peer_on_keepalive(p);
         else
@@ -379,8 +361,8 @@ net_state(struct peer *p, const char *buf)
             peer_set_in_state(p, BTP_MSGBODY, p->in.msg_len - 1);
         break;
     case BTP_PIECEMETA:
-        p->in.pc_index = net_read32(buf);
-        p->in.pc_begin = net_read32(buf + 4);
+        p->in.pc_index = dec_be32(buf);
+        p->in.pc_begin = dec_be32(buf + 4);
         peer_set_in_state(p, BTP_MSGBODY, p->in.msg_len - 9);
         break;
     case BTP_MSGBODY:
diff --git a/btpd/net.h b/btpd/net.h
index e162841..16e5da0 100644
--- a/btpd/net.h
+++ b/btpd/net.h
@@ -37,7 +37,4 @@ void net_write_cb(int sd, short type, void *arg);
 int net_connect2(struct sockaddr *sa, socklen_t salen, int *sd);
 int net_connect(const char *ip, int port, int *sd);
 
-void net_write32(void *buf, uint32_t num);
-uint32_t net_read32(const void *buf);
-
 #endif
diff --git a/btpd/net_buf.c b/btpd/net_buf.c
index 1206ef8..0be194c 100644
--- a/btpd/net_buf.c
+++ b/btpd/net_buf.c
@@ -53,7 +53,7 @@ static struct net_buf *
 nb_create_onesized(char mtype, int btype)
 {
     struct net_buf *out = nb_create_alloc(btype, 5);
-    net_write32(out->buf, 1);
+    enc_be32(out->buf, 1);
     out->buf[4] = mtype;
     return out;
 }
@@ -71,7 +71,7 @@ nb_create_keepalive(void)
 {
     if (m_keepalive == NULL) {
         m_keepalive = nb_create_alloc(NB_KEEPALIVE, 4);
-        net_write32(m_keepalive->buf, 0);
+        enc_be32(m_keepalive->buf, 0);
         nb_singleton(m_keepalive);
     }
     return m_keepalive;
@@ -82,10 +82,10 @@ nb_create_piece(uint32_t index, uint32_t begin, size_t blen)
 {
     struct net_buf *out;
     out = nb_create_alloc(NB_PIECE, 13);
-    net_write32(out->buf, 9 + blen);
+    enc_be32(out->buf, 9 + blen);
     out->buf[4] = MSG_PIECE;
-    net_write32(out->buf + 5, index);
-    net_write32(out->buf + 9, begin);
+    enc_be32(out->buf + 5, index);
+    enc_be32(out->buf + 9, begin);
     return out;
 }
 
@@ -116,11 +116,11 @@ struct net_buf *
 nb_create_request(uint32_t index, uint32_t begin, uint32_t length)
 {
     struct net_buf *out = nb_create_alloc(NB_REQUEST, 17);
-    net_write32(out->buf, 13);
+    enc_be32(out->buf, 13);
     out->buf[4] = MSG_REQUEST;
-    net_write32(out->buf + 5, index);
-    net_write32(out->buf + 9, begin);
-    net_write32(out->buf + 13, length);
+    enc_be32(out->buf + 5, index);
+    enc_be32(out->buf + 9, begin);
+    enc_be32(out->buf + 13, length);
     return out;
 }
 
@@ -128,11 +128,11 @@ struct net_buf *
 nb_create_cancel(uint32_t index, uint32_t begin, uint32_t length)
 {
     struct net_buf *out = nb_create_alloc(NB_CANCEL, 17);
-    net_write32(out->buf, 13);
+    enc_be32(out->buf, 13);
     out->buf[4] = MSG_CANCEL;
-    net_write32(out->buf + 5, index);
-    net_write32(out->buf + 9, begin);
-    net_write32(out->buf + 13, length);
+    enc_be32(out->buf + 5, index);
+    enc_be32(out->buf + 9, begin);
+    enc_be32(out->buf + 13, length);
     return out;
 }
 
@@ -140,9 +140,9 @@ struct net_buf *
 nb_create_have(uint32_t index)
 {
     struct net_buf *out = nb_create_alloc(NB_HAVE, 9);
-    net_write32(out->buf, 5);
+    enc_be32(out->buf, 5);
     out->buf[4] = MSG_HAVE;
-    net_write32(out->buf + 5, index);
+    enc_be32(out->buf + 5, index);
     return out;
 }
 
@@ -153,9 +153,9 @@ nb_create_multihave(struct torrent *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);
+            enc_be32(out->buf + count * 9, 5);
             out->buf[count * 9 + 4] = MSG_HAVE;
-            net_write32(out->buf + count * 9 + 5, i);
+            enc_be32(out->buf + count * 9 + 5, i);
             count++;
         }
     }
@@ -202,7 +202,7 @@ nb_create_bitfield(struct torrent *tp)
     uint32_t plen = ceil(tp->npieces / 8.0);
 
     struct net_buf *out = nb_create_alloc(NB_BITFIELD, 5);
-    net_write32(out->buf, plen + 1);
+    enc_be32(out->buf, plen + 1);
     out->buf[4] = MSG_BITFIELD;
     return out;
 }
@@ -234,7 +234,7 @@ nb_get_index(struct net_buf *nb)
     case NB_HAVE:
     case NB_PIECE:
     case NB_REQUEST:
-        return net_read32(nb->buf + 5);
+        return dec_be32(nb->buf + 5);
     default:
         abort();
     }
@@ -247,7 +247,7 @@ nb_get_begin(struct net_buf *nb)
     case NB_CANCEL:
     case NB_PIECE:
     case NB_REQUEST:
-        return net_read32(nb->buf + 9);
+        return dec_be32(nb->buf + 9);
     default:
         abort();
     }
@@ -259,9 +259,9 @@ nb_get_length(struct net_buf *nb)
     switch (nb->type) {
     case NB_CANCEL:
     case NB_REQUEST:
-        return net_read32(nb->buf + 13);
+        return dec_be32(nb->buf + 13);
     case NB_PIECE:
-        return net_read32(nb->buf) - 9;
+        return dec_be32(nb->buf) - 9;
     default:
         abort();
     }
diff --git a/btpd/tlib.c b/btpd/tlib.c
index 1c42d9f..be1f281 100644
--- a/btpd/tlib.c
+++ b/btpd/tlib.c
@@ -1,5 +1,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 
 #include <dirent.h>
 #include <fcntl.h>
@@ -265,7 +266,7 @@ id_test(const void *k1, const void *k2)
 static uint32_t
 id_hash(const void *k)
 {
-    return net_read32(k + 16);
+    return dec_be32(k + 16);
 }
 
 void
@@ -323,57 +324,131 @@ tlib_read_hash(struct tlib *tl, size_t off, uint32_t piece, uint8_t *hash)
 }
 
 int
-tlib_load_resume(struct tlib *tl, unsigned nfiles, struct file_time_size *fts,
-    size_t pfsize, uint8_t *pc_field, size_t bfsize, uint8_t *blk_field)
+tlib_load_mi(struct tlib *tl, char **res)
 {
-    int err, ver;
-    FILE *fp;
+    char file[PATH_MAX];
     char relpath[RELPATH_SIZE];
+    char *mi;
     bin2hex(tl->hash, relpath, 20);
-
-    if ((err = vfopen(&fp, "r" , "torrents/%s/resume", relpath)) != 0)
-        return err;
-
-    if (fscanf(fp, "%d\n", &ver) != 1)
-        goto invalid;
-    if (ver != 1)
-        goto invalid;
-    for (int i = 0; i < nfiles; i++) {
-        quad_t size;
-        long time;
-        if (fscanf(fp, "%qd %ld\n", &size, &time) != 2)
-            goto invalid;
-        fts[i].size = size;
-        fts[i].mtime = time;
+    snprintf(file, sizeof(file), "torrents/%s/torrent", relpath);
+    if ((mi = mi_load(file, NULL)) == NULL) {
+        btpd_log(BTPD_L_ERROR,
+            "torrent '%s': failed to load metainfo (%s).\n",
+            tl->name, strerror(errno));
+        return errno;
     }
-    if (fread(pc_field, 1, pfsize, fp) != pfsize)
-        goto invalid;
-    if (fread(blk_field, 1, bfsize, fp) != bfsize)
-        goto invalid;
-    fclose(fp);
+    *res = mi;
     return 0;
-invalid:
-    fclose(fp);
-    bzero(pc_field, pfsize);
-    bzero(blk_field, bfsize);
-    return EINVAL;
 }
 
-void
-tlib_save_resume(struct tlib *tl, unsigned nfiles, struct file_time_size *fts,
-    size_t pfsize, uint8_t *pc_field, size_t bfsize, uint8_t *blk_field)
+struct resume_data {
+    void *base;
+    size_t size;
+    uint8_t *pc_field;
+    uint8_t *blk_field;
+};
+
+static void *
+resume_file_size(struct resume_data *resd, int i)
 {
-    int err;
-    FILE *fp;
+    return resd->base + 8 + 16 * i;
+}
+
+static void *
+resume_file_time(struct resume_data *resd, int i)
+{
+    return resd->base + 16 + 16 * i;
+}
+
+static void
+init_resume(int fd, size_t size)
+{
+    char buf[1024];
+    uint32_t ver;
+    bzero(buf, sizeof(buf));
+    enc_be32(&ver, 2);
+    if (write(fd, "RESD", 4) == -1 || write(fd, &ver, 4) == -1)
+        goto fatal;
+    size -= 8;
+    while (size > 0) {
+        ssize_t nw = write(fd, buf, min(sizeof(buf), size));
+        if (nw < 1)
+            goto fatal;
+        size -= nw;
+    }
+    return;
+fatal:
+    btpd_err("failed to initialize resume file (%s).\n", strerror(errno));
+}
+
+struct resume_data *
+tlib_open_resume(struct tlib *tl, unsigned nfiles, size_t pfsize,
+    size_t bfsize)
+{
+    int fd;
     char relpath[RELPATH_SIZE];
+    struct stat sb;
+    struct resume_data *resd = btpd_calloc(1, sizeof(*resd));
     bin2hex(tl->hash, relpath, 20);
 
-    if ((err = vfopen(&fp, "wb", "torrents/%s/resume", relpath)) != 0)
-        return;
-    fprintf(fp, "%d\n", 1);
-    for (int i = 0; i < nfiles; i++)
-        fprintf(fp, "%lld %ld\n", (long long)fts[i].size, (long)fts[i].mtime);
-    fwrite(pc_field, 1, pfsize, fp);
-    fwrite(blk_field, 1, bfsize, fp);
-    if (fclose(fp) != 0); //XXX
+    resd->size = 8 + nfiles * 16 + pfsize + bfsize;
+
+    if ((errno =
+            vopen(&fd, O_RDWR|O_CREAT, "torrents/%s/resume", relpath)) != 0)
+        goto fatal;
+    if (fstat(fd, &sb) != 0)
+        goto fatal;
+    if (sb.st_size != resd->size) {
+        if (sb.st_size != 0 && ftruncate(fd, 0) != 0)
+            goto fatal;
+        init_resume(fd, resd->size);
+    }
+    resd->base =
+        mmap(NULL, resd->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+    if (resd->base == MAP_FAILED)
+        goto fatal;
+    if (bcmp(resd->base, "RESD", 4) != 0 || dec_be32(resd->base + 4) != 2)
+        init_resume(fd, resd->size);
+    close(fd);
+
+    resd->pc_field = resd->base + 8 + nfiles * 16;
+    resd->blk_field = resd->pc_field + pfsize;
+
+    return resd;
+fatal:
+    btpd_err("file operation failed on 'torrents/%s/resume' (%s).\n",
+        relpath, strerror(errno));
+}
+
+uint8_t *
+resume_piece_field(struct resume_data *resd)
+{
+    return resd->pc_field;
+}
+
+uint8_t *
+resume_block_field(struct resume_data *resd)
+{
+    return resd->blk_field;
+}
+
+void
+resume_set_fts(struct resume_data *resd, int i, struct file_time_size *fts)
+{
+    enc_be64(resume_file_size(resd, i), (uint64_t)fts->size);
+    enc_be64(resume_file_time(resd, i), (uint64_t)fts->mtime);
+}
+
+void
+resume_get_fts(struct resume_data *resd, int i, struct file_time_size *fts)
+{
+    fts->size = dec_be64(resume_file_size(resd, i));
+    fts->mtime = dec_be64(resume_file_time(resd, i));
+}
+
+void
+tlib_close_resume(struct resume_data *resd)
+{
+    munmap(resd->base, resd->size);
+    free(resd);
 }
diff --git a/btpd/tlib.h b/btpd/tlib.h
index 2373c80..74ae6f0 100644
--- a/btpd/tlib.h
+++ b/btpd/tlib.h
@@ -34,15 +34,20 @@ struct tlib *tlib_by_hash(const uint8_t *hash);
 struct tlib *tlib_by_num(unsigned num);
 unsigned tlib_count(void);
 
+int tlib_load_mi(struct tlib *tl, char **res);
+
 void tlib_read_hash(struct tlib *tl, size_t off, uint32_t piece,
     uint8_t *hash);
 
-int tlib_load_resume(struct tlib *tl, unsigned nfiles,
-    struct file_time_size *fts, size_t pfsize, uint8_t *pc_field,
-    size_t bfsize, uint8_t *blk_field);
+struct resume_data *tlib_open_resume(struct tlib *tl, unsigned nfiles,
+    size_t pfsize, size_t bfsize);
+void tlib_close_resume(struct resume_data *resume);
 
-void tlib_save_resume(struct tlib *tl, unsigned nfiles,
-    struct file_time_size *fts, size_t pfsize, uint8_t *pc_field,
-    size_t bfsize, uint8_t *blk_field);
+uint8_t *resume_piece_field(struct resume_data *resd);
+uint8_t *resume_block_field(struct resume_data *resd);
+void resume_set_fts(struct resume_data *resd, int i,
+    struct file_time_size *fts);
+void resume_get_fts(struct resume_data *resd, int i,
+    struct file_time_size *fts);
 
 #endif
diff --git a/btpd/torrent.c b/btpd/torrent.c
index e97a245..9c49ad3 100644
--- a/btpd/torrent.c
+++ b/btpd/torrent.c
@@ -1,7 +1,4 @@
 #include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -86,44 +83,14 @@ torrent_block_size(struct torrent *tp, uint32_t piece, uint32_t nblocks,
 enum ipc_err
 torrent_start(struct tlib *tl)
 {
-    struct stat sb;
     struct torrent *tp;
     char *mi;
-    char relpath[RELPATH_SIZE];
-    char file[PATH_MAX];
 
     if (tl->dir == NULL)
         return IPC_EBADTENT;
 
-    if (stat(tl->dir, &sb) == 0) {
-        if ((sb.st_mode & S_IFMT) != S_IFDIR) {
-            btpd_log(BTPD_L_ERROR,
-                "torrent '%s': content dir '%s' is not a directory\n",
-                tl->name, tl->dir);
-            return IPC_EBADCDIR;
-        }
-    } else if (errno == ENOENT) {
-        if (mkdirs(tl->dir, 0777) != 0 && errno != EEXIST) {
-            btpd_log(BTPD_L_ERROR, "torrent '%s': "
-                "failed to create content dir '%s' (%s).\n",
-                tl->name, tl->dir, strerror(errno));
-            return IPC_ECREATECDIR;
-        }
-    } else {
-        btpd_log(BTPD_L_ERROR,
-            "torrent '%s': couldn't stat content dir '%s' (%s)\n",
-            tl->name, tl->dir, strerror(errno));
-        return IPC_EBADCDIR;
-    }
-
-    bin2hex(tl->hash, relpath, 20);
-    snprintf(file, PATH_MAX, "torrents/%s/torrent", relpath);
-    if ((mi = mi_load(file, NULL)) == NULL) {
-        btpd_log(BTPD_L_ERROR,
-            "torrent '%s': failed to load metainfo (%s).\n",
-            tl->name, strerror(errno));
+    if (tlib_load_mi(tl, &mi) != 0)
         return IPC_EBADTENT;
-    }
 
     tp = btpd_calloc(1, sizeof(*tp));
     tp->tl = tl;
diff --git a/misc/subr.c b/misc/subr.c
index 22ed6e2..0ca41c7 100644
--- a/misc/subr.c
+++ b/misc/subr.c
@@ -14,6 +14,48 @@
 #include <unistd.h>
 
 void
+enc_be32(void *buf, uint32_t num)
+{
+    uint8_t *p = buf;
+    *p = (num >> 24) & 0xff;
+    *(p + 1) = (num >> 16) & 0xff;
+    *(p + 2) = (num >> 8) & 0xff;
+    *(p + 3) = num & 0xff;
+}
+
+uint32_t
+dec_be32(const void *buf)
+{
+    const uint8_t *p = buf;
+    return (uint32_t)*p << 24 | (uint32_t)*(p + 1) << 16
+        | (uint16_t)*(p + 2) << 8 | *(p + 3);
+}
+
+void
+enc_be64(void *buf, uint64_t num)
+{
+    uint8_t *p = buf;
+    *p = (num >> 56) & 0xff;
+    *(p + 1) = (num >> 48) & 0xff;
+    *(p + 2) = (num >> 40) & 0xff;
+    *(p + 3) = (num >> 32) & 0xff;
+    *(p + 4) = (num >> 24) & 0xff;
+    *(p + 5) = (num >> 16) & 0xff;
+    *(p + 6) = (num >> 8) & 0xff;
+    *(p + 7) = num & 0xff;
+}
+
+uint64_t
+dec_be64(const void *buf)
+{
+    const uint8_t *p = buf;
+    return (uint64_t)*p << 56 | (uint64_t)*(p + 1) << 48
+        | (uint64_t)*(p + 2) << 40 | (uint64_t)*(p + 3) << 32
+        | (uint64_t)*(p + 4) << 24 | (uint64_t)*(p + 5) << 16
+        | (uint64_t)*(p + 6) << 8 | (uint64_t)*(p + 7);
+}
+
+void
 set_bit(uint8_t *bits, unsigned long index)
 {
     bits[index / 8] |= (1 << (7 - index % 8));
diff --git a/misc/subr.h b/misc/subr.h
index e9eb088..d0bf831 100644
--- a/misc/subr.h
+++ b/misc/subr.h
@@ -9,6 +9,11 @@
 
 #define SHAHEXSIZE 41
 
+uint32_t dec_be32(const void *buf);
+uint64_t dec_be64(const void *buf);
+void enc_be32(void *buf, uint32_t num);
+void enc_be64(void *buf, uint64_t num);
+
 int set_nonblocking(int fd);
 int set_blocking(int fd);