bits as well. Implement them in libmisc instead of in btpd. o Change resume file format and related APIs. The resume files are now memory mapped.master
@@ -30,7 +30,7 @@ struct content { | |||||
struct bt_stream *rds; | struct bt_stream *rds; | ||||
struct bt_stream *wrs; | struct bt_stream *wrs; | ||||
struct event save_timer; | |||||
struct resume_data *resd; | |||||
}; | }; | ||||
#define ZEROBUFLEN (1 << 14) | #define ZEROBUFLEN (1 << 14) | ||||
@@ -103,8 +103,7 @@ void | |||||
cm_kill(struct torrent *tp) | cm_kill(struct torrent *tp) | ||||
{ | { | ||||
struct content *cm = tp->cm; | struct content *cm = tp->cm; | ||||
free(cm->piece_field); | |||||
free(cm->block_field); | |||||
tlib_close_resume(cm->resd); | |||||
free(cm->pos_field); | free(cm->pos_field); | ||||
free(cm); | free(cm); | ||||
tp->cm = NULL; | tp->cm = NULL; | ||||
@@ -117,9 +116,8 @@ cm_save(struct torrent *tp) | |||||
{ | { | ||||
struct file_time_size fts[tp->nfiles]; | struct file_time_size fts[tp->nfiles]; | ||||
stat_and_adjust(tp, fts); | 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 | static void | ||||
@@ -134,18 +132,18 @@ cm_on_error(struct torrent *tp) | |||||
static void | static void | ||||
cm_write_done(struct torrent *tp) | cm_write_done(struct torrent *tp) | ||||
{ | { | ||||
int err = 0; | |||||
int err; | |||||
struct content *cm = tp->cm; | 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", | btpd_log(BTPD_L_ERROR, "error closing write stream for '%s' (%s).\n", | ||||
torrent_name(tp), strerror(err)); | torrent_name(tp), strerror(err)); | ||||
cm->wrs = NULL; | |||||
btpd_ev_del(&cm->save_timer); | |||||
if (!err) | |||||
cm_save(tp); | |||||
else | |||||
cm_on_error(tp); | cm_on_error(tp); | ||||
} | |||||
if (!cm->error) | |||||
cm_save(tp); | |||||
} | } | ||||
void | void | ||||
@@ -195,27 +193,17 @@ cm_started(struct torrent *tp) | |||||
return cm->state == CM_ACTIVE; | 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 | void | ||||
cm_create(struct torrent *tp, const char *mi) | cm_create(struct torrent *tp, const char *mi) | ||||
{ | { | ||||
size_t pfield_size = ceil(tp->npieces / 8.0); | size_t pfield_size = ceil(tp->npieces / 8.0); | ||||
struct content *cm = btpd_calloc(1, sizeof(*cm)); | struct content *cm = btpd_calloc(1, sizeof(*cm)); | ||||
cm->bppbf = ceil((double)tp->piece_length / (1 << 17)); | 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->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; | tp->cm = cm; | ||||
} | } | ||||
@@ -431,9 +419,8 @@ startup_test_end(struct torrent *tp, int unclean) | |||||
if (unclean) { | if (unclean) { | ||||
struct start_test_data *std = BTPDQ_FIRST(&m_startq); | struct start_test_data *std = BTPDQ_FIRST(&m_startq); | ||||
BTPDQ_REMOVE(&m_startq, std, entry); | 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->fts); | ||||
free(std); | free(std); | ||||
} | } | ||||
@@ -447,7 +434,6 @@ startup_test_end(struct torrent *tp, int unclean) | |||||
cm_on_error(tp); | cm_on_error(tp); | ||||
return; | return; | ||||
} | } | ||||
btpd_ev_add(&cm->save_timer, SAVE_INTERVAL); | |||||
} | } | ||||
cm->state = CM_ACTIVE; | cm->state = CM_ACTIVE; | ||||
} | } | ||||
@@ -520,7 +506,7 @@ cm_start(struct torrent *tp, int force_test) | |||||
return; | return; | ||||
} | } | ||||
fts = btpd_calloc(tp->nfiles * 2, sizeof(*fts)); | |||||
fts = btpd_calloc(tp->nfiles, sizeof(*fts)); | |||||
if ((err = stat_and_adjust(tp, fts)) != 0) { | if ((err = stat_and_adjust(tp, fts)) != 0) { | ||||
free(fts); | free(fts); | ||||
@@ -528,13 +514,10 @@ cm_start(struct torrent *tp, int force_test) | |||||
return; | 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++) { | 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; | run_test = 1; | ||||
break; | break; | ||||
} | } | ||||
@@ -116,24 +116,6 @@ net_active(struct torrent *tp) | |||||
return tp->net->active; | 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 | #define BLOCK_MEM_COUNT 4 | ||||
static unsigned long | static unsigned long | ||||
@@ -256,7 +238,7 @@ net_dispatch_msg(struct peer *p, const char *buf) | |||||
peer_on_uninterest(p); | peer_on_uninterest(p); | ||||
break; | break; | ||||
case MSG_HAVE: | case MSG_HAVE: | ||||
peer_on_have(p, net_read32(buf)); | |||||
peer_on_have(p, dec_be32(buf)); | |||||
break; | break; | ||||
case MSG_BITFIELD: | case MSG_BITFIELD: | ||||
if (p->npieces == 0) | if (p->npieces == 0) | ||||
@@ -266,9 +248,9 @@ net_dispatch_msg(struct peer *p, const char *buf) | |||||
break; | break; | ||||
case MSG_REQUEST: | case MSG_REQUEST: | ||||
if ((p->flags & (PF_P_WANT|PF_I_CHOKE)) == PF_P_WANT) { | 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 | if ((length > PIECE_BLOCKLEN | ||||
|| index >= p->n->tp->npieces | || index >= p->n->tp->npieces | ||||
|| !cm_has_piece(p->n->tp, index) | || !cm_has_piece(p->n->tp, index) | ||||
@@ -282,9 +264,9 @@ net_dispatch_msg(struct peer *p, const char *buf) | |||||
} | } | ||||
break; | break; | ||||
case MSG_CANCEL: | 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); | peer_on_cancel(p, index, begin, length); | ||||
break; | break; | ||||
case MSG_PIECE: | case MSG_PIECE: | ||||
@@ -359,7 +341,7 @@ net_state(struct peer *p, const char *buf) | |||||
peer_set_in_state(p, BTP_MSGSIZE, 4); | peer_set_in_state(p, BTP_MSGSIZE, 4); | ||||
break; | break; | ||||
case BTP_MSGSIZE: | case BTP_MSGSIZE: | ||||
p->in.msg_len = net_read32(buf); | |||||
p->in.msg_len = dec_be32(buf); | |||||
if (p->in.msg_len == 0) | if (p->in.msg_len == 0) | ||||
peer_on_keepalive(p); | peer_on_keepalive(p); | ||||
else | 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); | peer_set_in_state(p, BTP_MSGBODY, p->in.msg_len - 1); | ||||
break; | break; | ||||
case BTP_PIECEMETA: | 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); | peer_set_in_state(p, BTP_MSGBODY, p->in.msg_len - 9); | ||||
break; | break; | ||||
case BTP_MSGBODY: | case BTP_MSGBODY: | ||||
@@ -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_connect2(struct sockaddr *sa, socklen_t salen, int *sd); | ||||
int net_connect(const char *ip, int port, 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 | #endif |
@@ -53,7 +53,7 @@ static struct net_buf * | |||||
nb_create_onesized(char mtype, int btype) | nb_create_onesized(char mtype, int btype) | ||||
{ | { | ||||
struct net_buf *out = nb_create_alloc(btype, 5); | struct net_buf *out = nb_create_alloc(btype, 5); | ||||
net_write32(out->buf, 1); | |||||
enc_be32(out->buf, 1); | |||||
out->buf[4] = mtype; | out->buf[4] = mtype; | ||||
return out; | return out; | ||||
} | } | ||||
@@ -71,7 +71,7 @@ nb_create_keepalive(void) | |||||
{ | { | ||||
if (m_keepalive == NULL) { | if (m_keepalive == NULL) { | ||||
m_keepalive = nb_create_alloc(NB_KEEPALIVE, 4); | m_keepalive = nb_create_alloc(NB_KEEPALIVE, 4); | ||||
net_write32(m_keepalive->buf, 0); | |||||
enc_be32(m_keepalive->buf, 0); | |||||
nb_singleton(m_keepalive); | nb_singleton(m_keepalive); | ||||
} | } | ||||
return 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; | struct net_buf *out; | ||||
out = nb_create_alloc(NB_PIECE, 13); | out = nb_create_alloc(NB_PIECE, 13); | ||||
net_write32(out->buf, 9 + blen); | |||||
enc_be32(out->buf, 9 + blen); | |||||
out->buf[4] = MSG_PIECE; | 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; | return out; | ||||
} | } | ||||
@@ -116,11 +116,11 @@ struct net_buf * | |||||
nb_create_request(uint32_t index, uint32_t begin, uint32_t length) | nb_create_request(uint32_t index, uint32_t begin, uint32_t length) | ||||
{ | { | ||||
struct net_buf *out = nb_create_alloc(NB_REQUEST, 17); | 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; | 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; | return out; | ||||
} | } | ||||
@@ -128,11 +128,11 @@ struct net_buf * | |||||
nb_create_cancel(uint32_t index, uint32_t begin, uint32_t length) | nb_create_cancel(uint32_t index, uint32_t begin, uint32_t length) | ||||
{ | { | ||||
struct net_buf *out = nb_create_alloc(NB_CANCEL, 17); | 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; | 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; | return out; | ||||
} | } | ||||
@@ -140,9 +140,9 @@ struct net_buf * | |||||
nb_create_have(uint32_t index) | nb_create_have(uint32_t index) | ||||
{ | { | ||||
struct net_buf *out = nb_create_alloc(NB_HAVE, 9); | 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; | out->buf[4] = MSG_HAVE; | ||||
net_write32(out->buf + 5, index); | |||||
enc_be32(out->buf + 5, index); | |||||
return out; | return out; | ||||
} | } | ||||
@@ -153,9 +153,9 @@ nb_create_multihave(struct torrent *tp) | |||||
struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * have_npieces); | struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * have_npieces); | ||||
for (uint32_t i = 0, count = 0; count < have_npieces; i++) { | for (uint32_t i = 0, count = 0; count < have_npieces; i++) { | ||||
if (cm_has_piece(tp, 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; | out->buf[count * 9 + 4] = MSG_HAVE; | ||||
net_write32(out->buf + count * 9 + 5, i); | |||||
enc_be32(out->buf + count * 9 + 5, i); | |||||
count++; | count++; | ||||
} | } | ||||
} | } | ||||
@@ -202,7 +202,7 @@ nb_create_bitfield(struct torrent *tp) | |||||
uint32_t plen = ceil(tp->npieces / 8.0); | uint32_t plen = ceil(tp->npieces / 8.0); | ||||
struct net_buf *out = nb_create_alloc(NB_BITFIELD, 5); | 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; | out->buf[4] = MSG_BITFIELD; | ||||
return out; | return out; | ||||
} | } | ||||
@@ -234,7 +234,7 @@ nb_get_index(struct net_buf *nb) | |||||
case NB_HAVE: | case NB_HAVE: | ||||
case NB_PIECE: | case NB_PIECE: | ||||
case NB_REQUEST: | case NB_REQUEST: | ||||
return net_read32(nb->buf + 5); | |||||
return dec_be32(nb->buf + 5); | |||||
default: | default: | ||||
abort(); | abort(); | ||||
} | } | ||||
@@ -247,7 +247,7 @@ nb_get_begin(struct net_buf *nb) | |||||
case NB_CANCEL: | case NB_CANCEL: | ||||
case NB_PIECE: | case NB_PIECE: | ||||
case NB_REQUEST: | case NB_REQUEST: | ||||
return net_read32(nb->buf + 9); | |||||
return dec_be32(nb->buf + 9); | |||||
default: | default: | ||||
abort(); | abort(); | ||||
} | } | ||||
@@ -259,9 +259,9 @@ nb_get_length(struct net_buf *nb) | |||||
switch (nb->type) { | switch (nb->type) { | ||||
case NB_CANCEL: | case NB_CANCEL: | ||||
case NB_REQUEST: | case NB_REQUEST: | ||||
return net_read32(nb->buf + 13); | |||||
return dec_be32(nb->buf + 13); | |||||
case NB_PIECE: | case NB_PIECE: | ||||
return net_read32(nb->buf) - 9; | |||||
return dec_be32(nb->buf) - 9; | |||||
default: | default: | ||||
abort(); | abort(); | ||||
} | } | ||||
@@ -1,5 +1,6 @@ | |||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <sys/mman.h> | |||||
#include <dirent.h> | #include <dirent.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
@@ -265,7 +266,7 @@ id_test(const void *k1, const void *k2) | |||||
static uint32_t | static uint32_t | ||||
id_hash(const void *k) | id_hash(const void *k) | ||||
{ | { | ||||
return net_read32(k + 16); | |||||
return dec_be32(k + 16); | |||||
} | } | ||||
void | void | ||||
@@ -323,57 +324,131 @@ tlib_read_hash(struct tlib *tl, size_t off, uint32_t piece, uint8_t *hash) | |||||
} | } | ||||
int | 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 relpath[RELPATH_SIZE]; | ||||
char *mi; | |||||
bin2hex(tl->hash, relpath, 20); | 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; | 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]; | char relpath[RELPATH_SIZE]; | ||||
struct stat sb; | |||||
struct resume_data *resd = btpd_calloc(1, sizeof(*resd)); | |||||
bin2hex(tl->hash, relpath, 20); | 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); | |||||
} | } |
@@ -34,15 +34,20 @@ struct tlib *tlib_by_hash(const uint8_t *hash); | |||||
struct tlib *tlib_by_num(unsigned num); | struct tlib *tlib_by_num(unsigned num); | ||||
unsigned tlib_count(void); | 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, | void tlib_read_hash(struct tlib *tl, size_t off, uint32_t piece, | ||||
uint8_t *hash); | 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 | #endif |
@@ -1,7 +1,4 @@ | |||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/mman.h> | |||||
#include <sys/stat.h> | |||||
#include <assert.h> | #include <assert.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
@@ -86,44 +83,14 @@ torrent_block_size(struct torrent *tp, uint32_t piece, uint32_t nblocks, | |||||
enum ipc_err | enum ipc_err | ||||
torrent_start(struct tlib *tl) | torrent_start(struct tlib *tl) | ||||
{ | { | ||||
struct stat sb; | |||||
struct torrent *tp; | struct torrent *tp; | ||||
char *mi; | char *mi; | ||||
char relpath[RELPATH_SIZE]; | |||||
char file[PATH_MAX]; | |||||
if (tl->dir == NULL) | if (tl->dir == NULL) | ||||
return IPC_EBADTENT; | 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; | return IPC_EBADTENT; | ||||
} | |||||
tp = btpd_calloc(1, sizeof(*tp)); | tp = btpd_calloc(1, sizeof(*tp)); | ||||
tp->tl = tl; | tp->tl = tl; | ||||
@@ -13,6 +13,48 @@ | |||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #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 | void | ||||
set_bit(uint8_t *bits, unsigned long index) | set_bit(uint8_t *bits, unsigned long index) | ||||
{ | { | ||||
@@ -9,6 +9,11 @@ | |||||
#define SHAHEXSIZE 41 | #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_nonblocking(int fd); | ||||
int set_blocking(int fd); | int set_blocking(int fd); | ||||