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.master
@@ -3,11 +3,11 @@ btpd_SOURCES=\ | |||||
main.c util.c\ | main.c util.c\ | ||||
btpd.c btpd.h\ | btpd.c btpd.h\ | ||||
opts.c opts.h\ | opts.c opts.h\ | ||||
cli_if.c\ | |||||
net.c net.h\ | net.c net.h\ | ||||
net_buf.c net_buf.h\ | net_buf.c net_buf.h\ | ||||
queue.h \ | queue.h \ | ||||
peer.c peer.h\ | peer.c peer.h\ | ||||
content.c content.h\ | |||||
download.c download_subr.c download.h\ | download.c download_subr.c download.h\ | ||||
torrent.c torrent.h\ | torrent.c torrent.h\ | ||||
tracker_req.c tracker_req.h\ | tracker_req.c tracker_req.h\ | ||||
@@ -52,7 +52,7 @@ btpd_shutdown(void) | |||||
tp = BTPDQ_FIRST(&m_torrents); | tp = BTPDQ_FIRST(&m_torrents); | ||||
while (tp != NULL) { | while (tp != NULL) { | ||||
struct torrent *next = BTPDQ_NEXT(tp, entry); | struct torrent *next = BTPDQ_NEXT(tp, entry); | ||||
torrent_unload(tp); | torrent_deactivate(tp); | ||||
tp = next; | tp = next; | ||||
} | } | ||||
btpd_log(BTPD_L_BTPD, "Exiting.\n"); | btpd_log(BTPD_L_BTPD, "Exiting.\n"); | ||||
@@ -136,6 +136,30 @@ btpd_get_peer_id(void) | |||||
return m_peer_id; | 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); | extern void ipc_init(void); | ||||
void | void | ||||
@@ -148,9 +172,15 @@ btpd_init(void) | |||||
m_peer_id[i] = rand_between(0, 255); | m_peer_id[i] = rand_between(0, 255); | ||||
net_init(); | net_init(); | ||||
ipc_init(); | //ipc_init(); | ||||
ul_init(); | ul_init(); | ||||
load_library(); | |||||
struct torrent *tp; | |||||
BTPDQ_FOREACH(tp, &m_torrents, entry) | |||||
torrent_activate(tp); | |||||
signal(SIGPIPE, SIG_IGN); | signal(SIGPIPE, SIG_IGN); | ||||
signal_set(&m_sigint, SIGINT, signal_cb, NULL); | signal_set(&m_sigint, SIGINT, signal_cb, NULL); | ||||
@@ -25,7 +25,7 @@ | |||||
#include "download.h" | #include "download.h" | ||||
#include "upload.h" | #include "upload.h" | ||||
#include "subr.h" | #include "subr.h" | ||||
#include "content.h" | |||||
#include "opts.h" | #include "opts.h" | ||||
#define BTPD_VERSION (PACKAGE_NAME "/" PACKAGE_VERSION) | #define BTPD_VERSION (PACKAGE_NAME "/" PACKAGE_VERSION) | ||||
@@ -1,5 +1,4 @@ | |||||
#include <sys/types.h> | #include <math.h> | ||||
#include <sys/mman.h> | |||||
#include "btpd.h" | #include "btpd.h" | ||||
#include "tracker_req.h" | #include "tracker_req.h" | ||||
@@ -7,6 +6,10 @@ | |||||
void | void | ||||
dl_start(struct torrent *tp) | 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 | void | ||||
@@ -15,6 +18,8 @@ dl_stop(struct torrent *tp) | |||||
struct piece *pc; | struct piece *pc; | ||||
while ((pc = BTPDQ_FIRST(&tp->getlst)) != NULL) | while ((pc = BTPDQ_FIRST(&tp->getlst)) != NULL) | ||||
piece_free(pc); | 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; | struct torrent *tp = p->tp; | ||||
tp->piece_count[index]++; | tp->piece_count[index]++; | ||||
if (has_bit(tp->piece_field, index)) | if (cm_has_piece(tp, index)) | ||||
return; | return; | ||||
struct piece *pc = dl_find_piece(tp, index); | struct piece *pc = dl_find_piece(tp, index); | ||||
if (tp->endgame) { | 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); | 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); | struct net_buf *have = nb_create_have(pc->index); | ||||
BTPDQ_FOREACH(p, &tp->peers, p_entry) | BTPDQ_FOREACH(p, &tp->peers, p_entry) | ||||
peer_send(p, have); | peer_send(p, have); | ||||
@@ -114,7 +115,7 @@ dl_on_ok_piece(struct piece *pc) | |||||
assert(pc->nreqs == 0); | assert(pc->nreqs == 0); | ||||
piece_free(pc); | piece_free(pc); | ||||
if (torrent_has_all(tp)) { | if (cm_full(tp)) { | ||||
btpd_log(BTPD_L_BTPD, "Finished: %s.\n", tp->relpath); | btpd_log(BTPD_L_BTPD, "Finished: %s.\n", tp->relpath); | ||||
tracker_req(tp, TR_COMPLETED); | tracker_req(tp, TR_COMPLETED); | ||||
BTPDQ_FOREACH(p, &tp->peers, p_entry) | 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", | btpd_log(BTPD_L_ERROR, "Bad hash for piece %u of %s.\n", | ||||
pc->index, tp->relpath); | 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->down_field, i); | ||||
clear_bit(pc->have_field, i); | |||||
} | |||||
pc->ngot = 0; | pc->ngot = 0; | ||||
pc->nbusy = 0; | pc->nbusy = 0; | ||||
msync(tp->imem, tp->isiz, MS_ASYNC); | |||||
if (tp->endgame) { | if (tp->endgame) { | ||||
struct peer *p; | struct peer *p; | ||||
@@ -177,10 +176,7 @@ dl_on_block(struct peer *p, struct block_request *req, | |||||
struct block *blk = req->blk; | struct block *blk = req->blk; | ||||
struct piece *pc = blk->pc; | struct piece *pc = blk->pc; | ||||
off_t cbegin = index * p->tp->meta.piece_length + begin; | cm_put_block(p->tp, index, begin / PIECE_BLOCKLEN, data); | ||||
torrent_put_bytes(p->tp, data, cbegin, length); | |||||
set_bit(pc->have_field, begin / PIECE_BLOCKLEN); | |||||
pc->ngot++; | pc->ngot++; | ||||
if (tp->endgame) { | if (tp->endgame) { | ||||
@@ -204,7 +200,7 @@ dl_on_block(struct peer *p, struct block_request *req, | |||||
} | } | ||||
BTPDQ_INIT(&blk->reqs); | BTPDQ_INIT(&blk->reqs); | ||||
if (pc->ngot == pc->nblocks) | if (pc->ngot == pc->nblocks) | ||||
dl_on_piece(pc); | cm_test_piece(pc); | ||||
} else { | } else { | ||||
BTPDQ_REMOVE(&blk->reqs, req, blk_entry); | BTPDQ_REMOVE(&blk->reqs, req, blk_entry); | ||||
free(req); | free(req); | ||||
@@ -213,7 +209,7 @@ dl_on_block(struct peer *p, struct block_request *req, | |||||
clear_bit(pc->down_field, begin / PIECE_BLOCKLEN); | clear_bit(pc->down_field, begin / PIECE_BLOCKLEN); | ||||
pc->nbusy--; | pc->nbusy--; | ||||
if (pc->ngot == pc->nblocks) | if (pc->ngot == pc->nblocks) | ||||
dl_on_piece(pc); | cm_test_piece(pc); | ||||
if (peer_leech_ok(p) && !peer_laden(p)) | if (peer_leech_ok(p) && !peer_laden(p)) | ||||
dl_assign_requests(p); | dl_assign_requests(p); | ||||
} | } | ||||
@@ -7,7 +7,6 @@ int piece_full(struct piece *pc); | |||||
void piece_free(struct piece *pc); | void piece_free(struct piece *pc); | ||||
void dl_on_piece_unfull(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_new_piece(struct torrent *tp, uint32_t index); | ||||
struct piece *dl_find_piece(struct torrent *tp, uint32_t index); | struct piece *dl_find_piece(struct torrent *tp, uint32_t index); | ||||
@@ -47,9 +47,7 @@ piece_alloc(struct torrent *tp, uint32_t index) | |||||
pc = btpd_calloc(1, mem); | pc = btpd_calloc(1, mem); | ||||
pc->tp = tp; | pc->tp = tp; | ||||
pc->down_field = (uint8_t *)(pc + 1); | pc->down_field = (uint8_t *)(pc + 1); | ||||
pc->have_field = | pc->have_field = cm_get_block_field(tp, index); | ||||
tp->block_field + | |||||
index * (size_t)ceil(tp->meta.piece_length / (double)(1 << 17)); | |||||
pc->index = index; | pc->index = index; | ||||
pc->nblocks = nblocks; | pc->nblocks = nblocks; | ||||
@@ -60,6 +58,7 @@ piece_alloc(struct torrent *tp, uint32_t index) | |||||
for (unsigned i = 0; i < nblocks; i++) | for (unsigned i = 0; i < nblocks; i++) | ||||
if (has_bit(pc->have_field, i)) | if (has_bit(pc->have_field, i)) | ||||
pc->ngot++; | pc->ngot++; | ||||
assert(pc->ngot < pc->nblocks); | |||||
pc->blocks = (struct block *)(pc->down_field + field); | pc->blocks = (struct block *)(pc->down_field + field); | ||||
for (unsigned i = 0; i < nblocks; i++) { | for (unsigned i = 0; i < nblocks; i++) { | ||||
@@ -108,7 +107,7 @@ static int | |||||
dl_should_enter_endgame(struct torrent *tp) | dl_should_enter_endgame(struct torrent *tp) | ||||
{ | { | ||||
int should; | 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; | should = 1; | ||||
struct piece *pc; | struct piece *pc; | ||||
BTPDQ_FOREACH(pc, &tp->getlst, entry) { | BTPDQ_FOREACH(pc, &tp->getlst, entry) { | ||||
@@ -195,79 +194,10 @@ dl_find_piece(struct torrent *tp, uint32_t index) | |||||
return pc; | return pc; | ||||
} | } | ||||
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 | static int | ||||
dl_piece_startable(struct peer *p, uint32_t index) | 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); | && !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, | * Called from dl_piece_assign_requests when a piece becomes full. | ||||
* when a pice becomes full. The wanted level of the peers | * The wanted level of the peers that has this piece will be decreased. | ||||
* that has this piece will be decreased. This function is | * This function is the only one that may trigger end game. | ||||
* the only one that may trigger end game. | |||||
*/ | */ | ||||
static void | static void | ||||
dl_on_piece_full(struct piece *pc) | dl_on_piece_full(struct piece *pc) | ||||
@@ -349,15 +278,7 @@ struct piece * | |||||
dl_new_piece(struct torrent *tp, uint32_t index) | dl_new_piece(struct torrent *tp, uint32_t index) | ||||
{ | { | ||||
btpd_log(BTPD_L_POL, "Started on piece %u.\n", index); | btpd_log(BTPD_L_POL, "Started on piece %u.\n", index); | ||||
struct piece *pc = piece_alloc(tp, index); | return 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; | |||||
} | } | ||||
/* | /* | ||||
@@ -57,6 +57,9 @@ setup_daemon(const char *dir) | |||||
if (chdir(dir) != 0) | if (chdir(dir) != 0) | ||||
err(1, "Couldn't change working directory to '%s'", dir); | 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); | pidfd = open("pid", O_CREAT|O_WRONLY|O_NONBLOCK|O_EXLOCK, 0666); | ||||
if (pidfd == -1) | if (pidfd == -1) | ||||
err(1, "Couldn't open 'pid'"); | err(1, "Couldn't open 'pid'"); | ||||
@@ -170,9 +173,9 @@ args_done: | |||||
event_init(); | event_init(); | ||||
btpd_init(); | btpd_init(); | ||||
torrent_load("test"); | |||||
event_dispatch(); | event_dispatch(); | ||||
btpd_err("Unexpected exit from libevent.\n"); | btpd_err("Unexpected exit from libevent.\n"); | ||||
return 1; | return 1; | ||||
@@ -208,7 +208,7 @@ net_dispatch_msg(struct peer *p, const char *buf) | |||||
length = net_read32(buf + 8); | length = net_read32(buf + 8); | ||||
if ((length > PIECE_BLOCKLEN | if ((length > PIECE_BLOCKLEN | ||||
|| index >= p->tp->meta.npieces | || 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))) { | || begin + length > torrent_piece_size(p->tp, index))) { | ||||
btpd_log(BTPD_L_MSG, "bad request: (%u, %u, %u) from %p\n", | btpd_log(BTPD_L_MSG, "bad request: (%u, %u, %u) from %p\n", | ||||
index, begin, length, p); | index, begin, length, p); | ||||
@@ -121,9 +121,10 @@ nb_create_have(uint32_t index) | |||||
struct net_buf * | struct net_buf * | ||||
nb_create_multihave(struct torrent *tp) | nb_create_multihave(struct torrent *tp) | ||||
{ | { | ||||
struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * tp->have_npieces); | uint32_t have_npieces = cm_get_npieces(tp); | ||||
for (uint32_t i = 0, count = 0; count < tp->have_npieces; i++) { | struct net_buf *out = nb_create_alloc(NB_MULTIHAVE, 9 * have_npieces); | ||||
if (has_bit(tp->piece_field, i)) { | for (uint32_t i = 0, count = 0; count < have_npieces; i++) { | ||||
if (cm_has_piece(tp, i)) { | |||||
net_write32(out->buf + count * 9, 5); | net_write32(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); | 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); | uint32_t plen = ceil(tp->meta.npieces / 8.0); | ||||
struct net_buf *out = | 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; | return out; | ||||
} | } | ||||
@@ -336,8 +336,8 @@ peer_on_shake(struct peer *p) | |||||
printid[i] = '\0'; | printid[i] = '\0'; | ||||
btpd_log(BTPD_L_MSG, "received shake(%s) from %p\n", printid, p); | 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)); | p->piece_field = btpd_calloc(1, (int)ceil(p->tp->meta.npieces / 8.0)); | ||||
if (p->tp->have_npieces > 0) { | if (cm_get_npieces(p->tp) > 0) { | ||||
if (p->tp->have_npieces * 9 < 5 + ceil(p->tp->meta.npieces / 8.0)) | if (cm_get_npieces(p->tp) * 9 < 5 + ceil(p->tp->meta.npieces / 8.0)) | ||||
peer_send(p, nb_create_multihave(p->tp)); | peer_send(p, nb_create_multihave(p->tp)); | ||||
else { | else { | ||||
peer_send(p, nb_create_bitfield(p->tp)); | 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", | btpd_log(BTPD_L_MSG, "received request(%u,%u,%u) from %p\n", | ||||
index, begin, length, p); | index, begin, length, p); | ||||
if ((p->flags & PF_NO_REQUESTS) == 0) { | if ((p->flags & PF_NO_REQUESTS) == 0) { | ||||
off_t cbegin = index * p->tp->meta.piece_length + begin; | char *content; | ||||
char * content = torrent_get_bytes(p->tp, cbegin, length); | 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_piece(index, begin, length)); | ||||
peer_send(p, nb_create_torrentdata(content, length)); | peer_send(p, nb_create_torrentdata(content, length)); | ||||
p->npiece_msgs++; | p->npiece_msgs++; | ||||
if (p->npiece_msgs >= MAXPIECEMSGS) { | if (p->npiece_msgs >= MAXPIECEMSGS) { | ||||
peer_send(p, nb_create_choke()); | peer_send(p, nb_create_choke()); | ||||
peer_send(p, nb_create_unchoke()); | peer_send(p, nb_create_unchoke()); | ||||
p->flags |= PF_NO_REQUESTS; | p->flags |= PF_NO_REQUESTS; | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -19,194 +19,6 @@ | |||||
#include "tracker_req.h" | #include "tracker_req.h" | ||||
#include "stream.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 | int | ||||
torrent_has_peer(struct torrent *tp, const uint8_t *id) | 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 | 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(); | |||||
} | |||||
} | } |
@@ -23,7 +23,7 @@ struct piece { | |||||
struct block *blocks; | struct block *blocks; | ||||
uint8_t *have_field; | const uint8_t *have_field; | ||||
uint8_t *down_field; | uint8_t *down_field; | ||||
BTPDQ_ENTRY(piece) entry; | BTPDQ_ENTRY(piece) entry; | ||||
@@ -31,26 +31,29 @@ struct piece { | |||||
BTPDQ_HEAD(piece_tq, piece); | BTPDQ_HEAD(piece_tq, piece); | ||||
enum torrent_state { | |||||
T_INACTIVE, | |||||
T_STARTING, | |||||
T_ACTIVE, | |||||
T_STOPPING | |||||
}; | |||||
struct torrent { | struct torrent { | ||||
const char *relpath; | const char *relpath; | ||||
struct metainfo meta; | struct metainfo meta; | ||||
enum torrent_state state; | |||||
struct content *cp; | |||||
BTPDQ_ENTRY(torrent) entry; | BTPDQ_ENTRY(torrent) entry; | ||||
BTPDQ_ENTRY(torrent) net_entry; | BTPDQ_ENTRY(torrent) net_entry; | ||||
void *imem; | |||||
size_t isiz; | |||||
int net_active; | int net_active; | ||||
uint8_t *piece_field; | |||||
uint8_t *block_field; | |||||
uint8_t *busy_field; | uint8_t *busy_field; | ||||
uint32_t npcs_busy; | uint32_t npcs_busy; | ||||
uint32_t have_npieces; | |||||
unsigned *piece_count; | unsigned *piece_count; | ||||
uint64_t uploaded, downloaded; | uint64_t uploaded, downloaded; | ||||
@@ -66,21 +69,21 @@ struct torrent { | |||||
BTPDQ_HEAD(torrent_tq, torrent); | BTPDQ_HEAD(torrent_tq, torrent); | ||||
off_t torrent_bytes_left(struct torrent *tp); | int torrent_create(struct torrent **res, const char *path); | ||||
void torrent_activate(struct torrent *tp); | |||||
char *torrent_get_bytes(struct torrent *tp, off_t start, size_t len); | void torrent_deactivate(struct torrent *tp); | ||||
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_has_peer(struct torrent *tp, const uint8_t *id); | int torrent_has_peer(struct torrent *tp, const uint8_t *id); | ||||
off_t torrent_piece_size(struct torrent *tp, uint32_t index); | off_t torrent_piece_size(struct torrent *tp, uint32_t index); | ||||
uint32_t torrent_block_size(struct piece *pc, 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 | #endif |
@@ -128,14 +128,9 @@ tracker_done(pid_t pid, void *arg) | |||||
} | } | ||||
out: | out: | ||||
if (failed) { | if (failed) | ||||
if (req->tr_event == TR_STARTED) { | ;//tp->tracker_time = btpd_seconds + 10; | ||||
btpd_log(BTPD_L_BTPD, | |||||
"Start request failed for %s.\n", tp->relpath); | |||||
torrent_unload(tp); | |||||
} else | |||||
;//tp->tracker_time = btpd_seconds + 10; | |||||
} | |||||
munmap(req->res, REQ_SIZE); | munmap(req->res, REQ_SIZE); | ||||
free(req); | 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(); | const uint8_t *peer_id = btpd_get_peer_id(); | ||||
char qc; | char qc; | ||||
int i; | int i; | ||||
uint64_t left; | off_t left; | ||||
const char *event; | const char *event; | ||||
event = event2str(req->tr_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++) | for (i = 0; i < 20; i++) | ||||
snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[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" | i = asprintf(url, "%s%cinfo_hash=%s" | ||||
"&peer_id=%s" | "&peer_id=%s" | ||||
@@ -19,8 +19,8 @@ rate_cmp(const void *arg1, const void *arg2) | |||||
{ | { | ||||
struct peer *p1 = (*(struct peer_sort **)arg1)->p; | struct peer *p1 = (*(struct peer_sort **)arg1)->p; | ||||
struct peer *p2 = (*(struct peer_sort **)arg2)->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 rate1 = cm_full(p1->tp) ? p1->rate_up : p1->rate_dwn; | ||||
unsigned long rate2 = torrent_has_all(p2->tp) ? p2->rate_up : p2->rate_dwn; | unsigned long rate2 = cm_full(p2->tp) ? p2->rate_up : p2->rate_dwn; | ||||
if (rate1 < rate2) | if (rate1 < rate2) | ||||
return -1; | return -1; | ||||
else if (rate1 == rate2) | else if (rate1 == rate2) | ||||
@@ -51,8 +51,8 @@ choke_do(void) | |||||
int unchoked[m_npeers]; | int unchoked[m_npeers]; | ||||
BTPDQ_FOREACH(p, &m_peerq, ul_entry) { | BTPDQ_FOREACH(p, &m_peerq, ul_entry) { | ||||
if (((torrent_has_all(p->tp) && p->rate_up > 0) | if (((cm_full(p->tp) && p->rate_up > 0) | ||||
|| (!torrent_has_all(p->tp) && p->rate_dwn > 0))) { | || (!cm_full(p->tp) && p->rate_dwn > 0))) { | ||||
worthy[nworthy].p = p; | worthy[nworthy].p = p; | ||||
worthy[nworthy].i = i; | worthy[nworthy].i = i; | ||||
nworthy++; | nworthy++; | ||||