A clone of btpd with my configuration changes.

544 lines
14 KiB

  1. #include "btpd.h"
  2. #include <openssl/sha.h>
  3. #include <stream.h>
  4. struct content {
  5. enum { CM_INACTIVE, CM_STARTING, CM_ACTIVE } state;
  6. int error;
  7. uint32_t npieces_got;
  8. off_t ncontent_bytes;
  9. size_t bppbf; // bytes per piece block field
  10. uint8_t *piece_field;
  11. uint8_t *block_field;
  12. uint8_t *pos_field;
  13. struct bt_stream *rds;
  14. struct bt_stream *wrs;
  15. struct resume_data *resd;
  16. };
  17. #define ZEROBUFLEN (1 << 14)
  18. static const uint8_t m_zerobuf[ZEROBUFLEN];
  19. static int
  20. fd_cb_rd(const char *path, int *fd, void *arg)
  21. {
  22. struct torrent *tp = arg;
  23. return vopen(fd, O_RDONLY, "%s/%s", tp->tl->dir, path);
  24. }
  25. static int
  26. fd_cb_wr(const char *path, int *fd, void *arg)
  27. {
  28. struct torrent *tp = arg;
  29. return vopen(fd, O_RDWR, "%s/%s", tp->tl->dir, path);
  30. }
  31. struct start_test_data {
  32. struct torrent *tp;
  33. struct file_time_size *fts;
  34. uint32_t start;
  35. BTPDQ_ENTRY(start_test_data) entry;
  36. };
  37. BTPDQ_HEAD(std_tq, start_test_data);
  38. static struct std_tq m_startq = BTPDQ_HEAD_INITIALIZER(m_startq);
  39. static struct timeout m_workev;
  40. #define READBUFLEN (1 << 14)
  41. static int
  42. test_hash(struct torrent *tp, uint8_t *hash, uint32_t piece)
  43. {
  44. char piece_hash[SHA_DIGEST_LENGTH];
  45. tlib_read_hash(tp->tl, tp->pieces_off, piece, piece_hash);
  46. return bcmp(hash, piece_hash, SHA_DIGEST_LENGTH);
  47. }
  48. static int
  49. test_piece(struct torrent *tp, uint32_t piece, int *ok)
  50. {
  51. int err;
  52. uint8_t hash[SHA_DIGEST_LENGTH];
  53. if ((err = bts_sha(tp->cm->rds, piece * tp->piece_length,
  54. torrent_piece_size(tp, piece), hash)) != 0) {
  55. btpd_log(BTPD_L_ERROR, "io error on '%s' (%s).\n",
  56. bts_filename(tp->cm->rds), strerror(err));
  57. return err;;
  58. }
  59. *ok = test_hash(tp, hash, piece) == 0;
  60. return 0;
  61. }
  62. static int test_hash(struct torrent *tp, uint8_t *hash, uint32_t piece);
  63. static void startup_test_run(void);
  64. void
  65. worker_cb(int fd, short type, void *arg)
  66. {
  67. startup_test_run();
  68. }
  69. void
  70. cm_kill(struct torrent *tp)
  71. {
  72. struct content *cm = tp->cm;
  73. tlib_close_resume(cm->resd);
  74. free(cm->pos_field);
  75. free(cm);
  76. tp->cm = NULL;
  77. }
  78. static int stat_and_adjust(struct torrent *tp, struct file_time_size ret[]);
  79. void
  80. cm_save(struct torrent *tp)
  81. {
  82. struct file_time_size fts[tp->nfiles];
  83. stat_and_adjust(tp, fts);
  84. for (int i = 0; i < tp->nfiles; i++)
  85. resume_set_fts(tp->cm->resd, i, fts + i);
  86. }
  87. static void
  88. cm_on_error(struct torrent *tp)
  89. {
  90. if (!tp->cm->error) {
  91. tp->cm->error = 1;
  92. cm_stop(tp);
  93. }
  94. }
  95. static void
  96. cm_write_done(struct torrent *tp)
  97. {
  98. int err;
  99. struct content *cm = tp->cm;
  100. err = bts_close(cm->wrs);
  101. cm->wrs = NULL;
  102. if (err && !cm->error) {
  103. btpd_log(BTPD_L_ERROR, "error closing write stream for '%s' (%s).\n",
  104. torrent_name(tp), strerror(err));
  105. cm_on_error(tp);
  106. }
  107. if (!cm->error)
  108. cm_save(tp);
  109. }
  110. void
  111. cm_stop(struct torrent *tp)
  112. {
  113. struct content *cm = tp->cm;
  114. if (cm->state != CM_STARTING && cm->state != CM_ACTIVE)
  115. return;
  116. if (cm->state == CM_STARTING) {
  117. struct start_test_data *std;
  118. BTPDQ_FOREACH(std, &m_startq, entry)
  119. if (std->tp == tp) {
  120. BTPDQ_REMOVE(&m_startq, std, entry);
  121. free(std->fts);
  122. free(std);
  123. break;
  124. }
  125. }
  126. if (cm->rds != NULL)
  127. bts_close(cm->rds);
  128. if (cm->wrs != NULL)
  129. cm_write_done(tp);
  130. cm->state = CM_INACTIVE;
  131. }
  132. int
  133. cm_active(struct torrent *tp)
  134. {
  135. struct content *cm = tp->cm;
  136. return cm->state != CM_INACTIVE;
  137. }
  138. int
  139. cm_error(struct torrent *tp)
  140. {
  141. return tp->cm->error;
  142. }
  143. int
  144. cm_started(struct torrent *tp)
  145. {
  146. struct content *cm = tp->cm;
  147. return cm->state == CM_ACTIVE;
  148. }
  149. void
  150. cm_create(struct torrent *tp, const char *mi)
  151. {
  152. size_t pfield_size = ceil(tp->npieces / 8.0);
  153. struct content *cm = btpd_calloc(1, sizeof(*cm));
  154. cm->bppbf = ceil((double)tp->piece_length / (1 << 17));
  155. cm->pos_field = btpd_calloc(pfield_size, 1);
  156. cm->resd = tlib_open_resume(tp->tl, tp->nfiles, pfield_size,
  157. cm->bppbf * tp->npieces);
  158. cm->piece_field = resume_piece_field(cm->resd);
  159. cm->block_field = resume_block_field(cm->resd);
  160. tp->cm = cm;
  161. }
  162. int
  163. cm_get_bytes(struct torrent *tp, uint32_t piece, uint32_t begin, size_t len,
  164. uint8_t **buf)
  165. {
  166. if (tp->cm->error)
  167. return EIO;
  168. *buf = btpd_malloc(len);
  169. int err =
  170. bts_get(tp->cm->rds, piece * tp->piece_length + begin, *buf, len);
  171. if (err != 0) {
  172. btpd_log(BTPD_L_ERROR, "io error on '%s' (%s).\n",
  173. bts_filename(tp->cm->rds), strerror(err));
  174. cm_on_error(tp);
  175. }
  176. return err;
  177. }
  178. void
  179. cm_prealloc(struct torrent *tp, uint32_t piece)
  180. {
  181. struct content *cm = tp->cm;
  182. if (cm_alloc_size <= 0)
  183. set_bit(cm->pos_field, piece);
  184. }
  185. void
  186. cm_test_piece(struct torrent *tp, uint32_t piece)
  187. {
  188. int ok;
  189. struct content *cm = tp->cm;
  190. if ((errno = test_piece(tp, piece, &ok)) != 0)
  191. cm_on_error(tp);
  192. else if (ok) {
  193. assert(cm->npieces_got < tp->npieces);
  194. cm->npieces_got++;
  195. set_bit(cm->piece_field, piece);
  196. if (net_active(tp))
  197. dl_on_ok_piece(tp->net,piece);
  198. if (cm_full(tp))
  199. cm_write_done(tp);
  200. } else {
  201. cm->ncontent_bytes -= torrent_piece_size(tp,piece);
  202. bzero(cm->block_field + piece * cm->bppbf, cm->bppbf);
  203. if (net_active(tp))
  204. dl_on_bad_piece(tp->net, piece);
  205. }
  206. }
  207. int
  208. cm_put_bytes(struct torrent *tp, uint32_t piece, uint32_t begin,
  209. const uint8_t *buf, size_t len)
  210. {
  211. int err;
  212. struct content *cm = tp->cm;
  213. if (cm->error)
  214. return EIO;
  215. uint8_t *bf = cm->block_field + piece * cm->bppbf;
  216. assert(!has_bit(bf, begin / PIECE_BLOCKLEN));
  217. assert(!has_bit(cm->piece_field, piece));
  218. if (!has_bit(cm->pos_field, piece)) {
  219. unsigned npieces = ceil((double)cm_alloc_size / tp->piece_length);
  220. uint32_t start = piece - piece % npieces;
  221. uint32_t end = min(start + npieces, tp->npieces);
  222. while (start < end) {
  223. if (!has_bit(cm->pos_field, start)) {
  224. assert(!has_bit(cm->piece_field, start));
  225. off_t len = torrent_piece_size(tp, start);
  226. off_t off = tp->piece_length * start;
  227. while (len > 0) {
  228. size_t wlen = min(ZEROBUFLEN, len);
  229. if ((err = bts_put(cm->wrs, off, m_zerobuf, wlen)) != 0) {
  230. btpd_log(BTPD_L_ERROR, "io error on '%s' (%s).\n",
  231. bts_filename(cm->wrs), strerror(errno));
  232. cm_on_error(tp);
  233. return err;
  234. }
  235. len -= wlen;
  236. off += wlen;
  237. }
  238. set_bit(cm->pos_field, start);
  239. }
  240. start++;
  241. }
  242. }
  243. err = bts_put(cm->wrs, piece * tp->piece_length + begin, buf, len);
  244. if (err != 0) {
  245. btpd_log(BTPD_L_ERROR, "io error on '%s' (%s)\n",
  246. bts_filename(cm->wrs), strerror(err));
  247. cm_on_error(tp);
  248. return err;
  249. }
  250. cm->ncontent_bytes += len;
  251. set_bit(bf, begin / PIECE_BLOCKLEN);
  252. return 0;
  253. }
  254. int
  255. cm_full(struct torrent *tp)
  256. {
  257. return tp->cm->npieces_got == tp->npieces;
  258. }
  259. off_t
  260. cm_content(struct torrent *tp)
  261. {
  262. return tp->cm->ncontent_bytes;
  263. }
  264. uint32_t
  265. cm_pieces(struct torrent *tp)
  266. {
  267. return tp->cm->npieces_got;
  268. }
  269. uint8_t *
  270. cm_get_piece_field(struct torrent *tp)
  271. {
  272. return tp->cm->piece_field;
  273. }
  274. uint8_t *
  275. cm_get_block_field(struct torrent *tp, uint32_t piece)
  276. {
  277. return tp->cm->block_field + piece * tp->cm->bppbf;
  278. }
  279. int
  280. cm_has_piece(struct torrent *tp, uint32_t piece)
  281. {
  282. return has_bit(tp->cm->piece_field, piece);
  283. }
  284. int
  285. stat_and_adjust(struct torrent *tp, struct file_time_size ret[])
  286. {
  287. int fd;
  288. char path[PATH_MAX];
  289. struct stat sb;
  290. for (int i = 0; i < tp->nfiles; i++) {
  291. snprintf(path, PATH_MAX, "%s/%s", tp->tl->dir, tp->files[i].path);
  292. again:
  293. if (stat(path, &sb) == -1) {
  294. if (errno == ENOENT) {
  295. errno = vopen(&fd, O_CREAT|O_RDWR, "%s", path);
  296. if (errno != 0 || close(fd) != 0) {
  297. btpd_log(BTPD_L_ERROR, "failed to create '%s' (%s).\n",
  298. path, strerror(errno));
  299. return errno;
  300. }
  301. goto again;
  302. } else {
  303. btpd_log(BTPD_L_ERROR, "failed to stat '%s' (%s).\n",
  304. path, strerror(errno));
  305. return errno;
  306. }
  307. } else if (sb.st_size > tp->files[i].length) {
  308. if (truncate(path, tp->files[i].length) != 0) {
  309. btpd_log(BTPD_L_ERROR, "failed to truncate '%s' (%s).\n",
  310. path, strerror(errno));
  311. return errno;
  312. }
  313. goto again;
  314. } else {
  315. ret[i].mtime = sb.st_mtime;
  316. ret[i].size = sb.st_size;
  317. }
  318. }
  319. return 0;
  320. }
  321. void
  322. startup_test_end(struct torrent *tp, int unclean)
  323. {
  324. struct content *cm = tp->cm;
  325. bzero(cm->pos_field, ceil(tp->npieces / 8.0));
  326. for (uint32_t piece = 0; piece < tp->npieces; piece++) {
  327. if (cm_has_piece(tp, piece)) {
  328. cm->ncontent_bytes += torrent_piece_size(tp, piece);
  329. cm->npieces_got++;
  330. set_bit(cm->pos_field, piece);
  331. continue;
  332. }
  333. uint8_t *bf = cm->block_field + cm->bppbf * piece;
  334. uint32_t nblocks = torrent_piece_blocks(tp, piece);
  335. uint32_t nblocks_got = 0;
  336. for (uint32_t i = 0; i < nblocks; i++) {
  337. if (has_bit(bf, i)) {
  338. nblocks_got++;
  339. cm->ncontent_bytes +=
  340. torrent_block_size(tp, piece, nblocks, i);
  341. }
  342. }
  343. if (nblocks_got == nblocks) {
  344. bzero(bf, cm->bppbf);
  345. cm->ncontent_bytes -= torrent_piece_size(tp, piece);
  346. } else if (nblocks_got > 0)
  347. set_bit(cm->pos_field, piece);
  348. }
  349. if (unclean) {
  350. struct start_test_data *std = BTPDQ_FIRST(&m_startq);
  351. BTPDQ_REMOVE(&m_startq, std, entry);
  352. for (int i = 0; i < tp->nfiles; i++)
  353. resume_set_fts(cm->resd, i, std->fts + i);
  354. free(std->fts);
  355. free(std);
  356. }
  357. if (!cm_full(tp)) {
  358. int err;
  359. if ((err = bts_open(&cm->wrs, tp->nfiles, tp->files,
  360. fd_cb_wr, tp)) != 0) {
  361. btpd_log(BTPD_L_ERROR,
  362. "failed to open write stream for '%s' (%s).\n",
  363. torrent_name(tp), strerror(err));
  364. cm_on_error(tp);
  365. return;
  366. }
  367. }
  368. cm->state = CM_ACTIVE;
  369. }
  370. void
  371. startup_test_run(void)
  372. {
  373. int ok;
  374. struct torrent *tp;
  375. struct content *cm;
  376. struct start_test_data * std = BTPDQ_FIRST(&m_startq);
  377. uint32_t this;
  378. if (std == NULL)
  379. return;
  380. tp = std->tp;
  381. cm = tp->cm;
  382. if (test_piece(std->tp, std->start, &ok) != 0) {
  383. cm_on_error(std->tp);
  384. return;
  385. }
  386. if (ok)
  387. set_bit(cm->piece_field, std->start);
  388. else
  389. clear_bit(cm->piece_field, std->start);
  390. this = std->start;
  391. do
  392. std->start++;
  393. while (std->start < tp->npieces && !has_bit(cm->pos_field, std->start));
  394. if (std->start >= tp->npieces)
  395. startup_test_end(tp, 1);
  396. if (!BTPDQ_EMPTY(&m_startq))
  397. btpd_timer_add(&m_workev, (& (struct timespec) { 0, 0 }));
  398. }
  399. void
  400. startup_test_begin(struct torrent *tp, struct file_time_size *fts)
  401. {
  402. uint32_t piece = 0;
  403. struct content *cm = tp->cm;
  404. while (piece < tp->npieces && !has_bit(cm->pos_field, piece))
  405. piece++;
  406. if (piece < tp->npieces) {
  407. struct start_test_data *std = btpd_calloc(1, sizeof(*std));
  408. std->tp = tp;
  409. std->start = piece;
  410. std->fts = fts;
  411. BTPDQ_INSERT_TAIL(&m_startq, std, entry);
  412. if (std == BTPDQ_FIRST(&m_startq))
  413. btpd_timer_add(&m_workev, (& (struct timespec) { 0, 0 }));
  414. } else {
  415. free(fts);
  416. startup_test_end(tp, 0);
  417. }
  418. }
  419. void
  420. cm_start(struct torrent *tp, int force_test)
  421. {
  422. int err, run_test = force_test;
  423. struct file_time_size *fts;
  424. struct content *cm = tp->cm;
  425. cm->state = CM_STARTING;
  426. if ((errno =
  427. bts_open(&cm->rds, tp->nfiles, tp->files, fd_cb_rd, tp)) != 0) {
  428. btpd_log(BTPD_L_ERROR, "failed to open stream for '%s' (%s).\n",
  429. torrent_name(tp), strerror(errno));
  430. cm_on_error(tp);
  431. return;
  432. }
  433. fts = btpd_calloc(tp->nfiles, sizeof(*fts));
  434. if ((err = stat_and_adjust(tp, fts)) != 0) {
  435. free(fts);
  436. cm_on_error(tp);
  437. return;
  438. }
  439. for (int i = 0; i < tp->nfiles; i++) {
  440. struct file_time_size rfts;
  441. resume_get_fts(cm->resd, i, &rfts);
  442. if ((fts[i].mtime != rfts.mtime || fts[i].size != rfts.size)) {
  443. run_test = 1;
  444. break;
  445. }
  446. }
  447. if (run_test) {
  448. memset(cm->pos_field, 0xff, ceil(tp->npieces / 8.0));
  449. off_t off = 0;
  450. for (int i = 0; i < tp->nfiles; i++) {
  451. if (fts[i].size != tp->files[i].length) {
  452. uint32_t start, end;
  453. end = (off + tp->files[i].length - 1)
  454. / tp->piece_length;
  455. start = (off + fts[i].size) / tp->piece_length;
  456. while (start <= end) {
  457. clear_bit(cm->pos_field, start);
  458. clear_bit(cm->piece_field, start);
  459. bzero(cm->block_field + start * cm->bppbf, cm->bppbf);
  460. start++;
  461. }
  462. }
  463. off += tp->files[i].length;
  464. }
  465. }
  466. startup_test_begin(tp, fts);
  467. }
  468. void
  469. cm_init(void)
  470. {
  471. timer_init(&m_workev, worker_cb, NULL);
  472. }