Verify bittorrent .torrent metainfo files.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

479 lines
14KB

  1. #include <errno.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <assert.h>
  7. #include "verify.h"
  8. #include "sha1.h"
  9. #include "opts.h"
  10. #ifdef MT
  11. #include <sys/sysinfo.h>
  12. #include <pthread.h>
  13. #include <semaphore.h>
  14. typedef struct {
  15. uint8_t* piece_data;
  16. int piece_data_size;
  17. int piece_index;
  18. const sha1sum_t* expected_result;
  19. int results_match;
  20. int done;
  21. sem_t sem_filled_buffer;
  22. } verify_thread_data_t;
  23. typedef struct {
  24. pthread_t thread;
  25. verify_thread_data_t thread_data;
  26. } verify_thread_t;
  27. static int mt_max_thread = 0;
  28. static verify_thread_t* mt_threads = NULL;
  29. /* Which thread data to fill? */
  30. static verify_thread_data_t* mt_td_tofill = NULL;
  31. static pthread_cond_t mt_cond_tofill;
  32. /* Mutex for _tofill */
  33. static pthread_mutex_t mt_mut_tofill;
  34. /* Worker thread signals to main thread to fill it's buffer */
  35. static sem_t mt_sem_needs_fill;
  36. #endif
  37. /*
  38. * Check if file, or directory exists
  39. * Return 0 if yes, or errno
  40. */
  41. static int verify_file_exists(const char* path) {
  42. if (access(path, F_OK|R_OK) == 0)
  43. return 0;
  44. return errno;
  45. }
  46. /*
  47. * Uselessly complex function to get the file path into a stack
  48. * buffer if the size is enough, or allocate one and copy it there
  49. * heap_str needs to be freed, if it's not null
  50. * Returns a pointer to the path string
  51. */
  52. static char* verify_get_path(fileinfo_t* finfo, const char* data_dir, \
  53. size_t data_dir_len, const char* torrent_name, int torrent_name_len, \
  54. char* stack_str, size_t stack_str_size, \
  55. char** heap_str, size_t* heap_str_size) {
  56. int path_len = metainfo_fileinfo_path(finfo, NULL);
  57. int req_len = path_len + data_dir_len + torrent_name_len + 1 + 1;
  58. char* path_ptr = stack_str;
  59. if (req_len > stack_str_size) {
  60. /* Stack is not large enough, use the heap */
  61. if (!(*heap_str)) {
  62. /* Heap is not yet allocated */
  63. *heap_str_size = req_len;
  64. path_ptr = *heap_str = malloc(*heap_str_size);
  65. } else if (path_len > *heap_str_size) {
  66. /* Heap size is not large enough, reallocate */
  67. *heap_str_size = req_len;
  68. path_ptr = *heap_str = realloc(*heap_str, *heap_str_size);
  69. } else {
  70. /* Heap is allocated, and is large enough */
  71. path_ptr = *heap_str;
  72. }
  73. }
  74. char* path_ptr_curr = path_ptr;
  75. memcpy(path_ptr_curr, data_dir, data_dir_len);
  76. path_ptr_curr += data_dir_len;
  77. /* WARNING: Not portable here */
  78. *path_ptr_curr++ = '/';
  79. memcpy(path_ptr_curr, torrent_name, torrent_name_len);
  80. path_ptr_curr += torrent_name_len;
  81. /* This may include multiple /'s but idc lol */
  82. *path_ptr_curr++ = '/';
  83. path_ptr_curr += metainfo_fileinfo_path(finfo, path_ptr_curr);
  84. *path_ptr_curr = '\0';
  85. return path_ptr;
  86. }
  87. typedef int (*fullpath_iter_cb)(const char* path, void* data);
  88. /*
  89. * Call the callback function with every full path
  90. * in the torrent. If callbacks returns non-zero, terminate the iter
  91. * If append_torrent_folder is 1 and the torrent is a multi file one,
  92. * the torrent name will be appended after data_dir
  93. */
  94. static int verify_fullpath_iter(metainfo_t* m, const char* data_dir, \
  95. int append_torrent_folder, fullpath_iter_cb cb, void* cb_data) {
  96. /* A sensible default on the stack */
  97. char path_buffer[512];
  98. /* If the above buffer is too small, malloc one */
  99. char* path_heap_ptr = NULL;
  100. size_t path_heap_size;
  101. const char* torrent_folder = "";
  102. int torrent_folder_len = 0;
  103. int result = 0;
  104. size_t data_dir_len = strlen(data_dir);
  105. fileinfo_t finfo;
  106. if (metainfo_is_multi_file(m)) {
  107. fileiter_t fiter;
  108. if (append_torrent_folder)
  109. metainfo_name(m, &torrent_folder, &torrent_folder_len);
  110. metainfo_fileiter_create(m, &fiter);
  111. while (result == 0 && metainfo_file_next(&fiter, &finfo) == 0) {
  112. char* path = verify_get_path(&finfo, data_dir, data_dir_len, \
  113. torrent_folder, torrent_folder_len, path_buffer, \
  114. sizeof(path_buffer), &path_heap_ptr, &path_heap_size);
  115. result = cb(path, cb_data);
  116. }
  117. } else {
  118. metainfo_fileinfo(m, &finfo);
  119. char* path = verify_get_path(&finfo, data_dir, data_dir_len, \
  120. torrent_folder, torrent_folder_len, path_buffer, \
  121. sizeof(path_buffer), &path_heap_ptr, &path_heap_size);
  122. result = cb(path, cb_data);
  123. }
  124. if (path_heap_ptr)
  125. free(path_heap_ptr);
  126. return result;
  127. }
  128. static int verify_is_files_exists_cb(const char* path, void* data) {
  129. return verify_file_exists(path);
  130. }
  131. /*
  132. * Check if the files in the torrent exists, or not
  133. * If append_torrent_folder is 1 and the torrent is a multi file one,
  134. * the torrent name will be appended after data_dir
  135. * Return 0 if yes, and is readable, or an errno
  136. */
  137. static int verify_is_files_exists(metainfo_t* m, const char* data_dir, \
  138. int append_torrent_folder) {
  139. return verify_fullpath_iter(m, data_dir, append_torrent_folder, \
  140. verify_is_files_exists_cb, NULL);
  141. }
  142. /*
  143. * Read in 1 piece size amount of data
  144. * Returns 0 if buffer got filled, -1 if error and 1 if end of the file
  145. */
  146. static int verify_read_piece(const char* path, FILE** f, int piece_size, \
  147. uint8_t* out_bytes, int* out_bytes_size) {
  148. if (!*f) {
  149. /* If first file, open it */
  150. *f = fopen(path, "rb");
  151. if (!*f)
  152. return -1;
  153. }
  154. int read;
  155. out_bytes += *out_bytes_size;
  156. while (*out_bytes_size != piece_size && (read = fread(out_bytes, \
  157. 1, piece_size - *out_bytes_size, *f)) > 0) {
  158. *out_bytes_size += read;
  159. out_bytes += read;
  160. }
  161. if (ferror(*f)) {
  162. /* If end because of an error */
  163. fclose(*f);
  164. *f = NULL;
  165. return -1;
  166. }
  167. if (feof(*f)) {
  168. /* If we reached the end of the current file */
  169. fclose(*f);
  170. *f = NULL;
  171. return 1;
  172. }
  173. /* If we filled the buffer */
  174. return 0;
  175. }
  176. typedef struct {
  177. metainfo_t* metai;
  178. int piece_size;
  179. uint8_t* piece_data;
  180. int piece_data_size;
  181. #ifdef MT
  182. const sha1sum_t* expected_piece_hash;
  183. #endif
  184. int piece_index;
  185. int file_count, file_index;
  186. } verify_files_data_t;
  187. #ifdef MT
  188. static void verify_piece_hash_mt_cond_cleanup(void* arg) {
  189. pthread_mutex_unlock(&mt_mut_tofill);
  190. }
  191. static void* verify_piece_hash_mt(void* param) {
  192. verify_thread_data_t* data = (verify_thread_data_t*)param;
  193. for (;;) {
  194. /* Wait until we can put out pointer into the tofill pointer */
  195. pthread_mutex_lock(&mt_mut_tofill);
  196. while (mt_td_tofill != NULL) {
  197. /* If we got cancelled when waiting on cond, mutex remains
  198. * locked and thus, deadlock ensures */
  199. pthread_cleanup_push(verify_piece_hash_mt_cond_cleanup, NULL);
  200. pthread_cond_wait(&mt_cond_tofill, &mt_mut_tofill);
  201. pthread_cleanup_pop(0);
  202. }
  203. mt_td_tofill = data;
  204. /* Ask main to fill out buffer */
  205. sem_post(&mt_sem_needs_fill);
  206. pthread_mutex_unlock(&mt_mut_tofill);
  207. /* Wait for it to be filled */
  208. if (sem_wait(&data->sem_filled_buffer) == -1) {
  209. if (errno == EINTR)
  210. break;
  211. }
  212. /* Work on the data */
  213. sha1sum_t result;
  214. SHA1_CTX ctx;
  215. SHA1Init(&ctx);
  216. SHA1Update(&ctx, data->piece_data, data->piece_data_size);
  217. SHA1Final(result, &ctx);
  218. /* Compare the data */
  219. if (memcmp(result, data->expected_result, sizeof(sha1sum_t)) != 0) {
  220. data->results_match = 0;
  221. } else {
  222. data->results_match = 1;
  223. }
  224. }
  225. return 0;
  226. }
  227. #else
  228. static void verify_piece_hash(uint8_t* piece_data, int piece_size, sha1sum_t result) {
  229. SHA1_CTX ctx;
  230. SHA1Init(&ctx);
  231. SHA1Update(&ctx, piece_data, piece_size);
  232. SHA1Final(result, &ctx);
  233. }
  234. #endif
  235. #ifdef MT
  236. #include <time.h>
  237. /* MT magic oOoOOoOOoOoo */
  238. static int verify_files_cb(const char* path, void* data) {
  239. verify_files_data_t* vfi = (verify_files_data_t*)data;
  240. FILE* f = NULL;
  241. int read_piece_result = 0;
  242. int result = 0;
  243. if (!opt_silent) {
  244. vfi->file_index++;
  245. printf("[%d/%d] Verifying file: %s\n", vfi->file_index, vfi->file_count, path);
  246. }
  247. for (;;) {
  248. /* If we don't have enough data to give to the threads, read it here */
  249. if (vfi->piece_data_size != vfi->piece_size) {
  250. read_piece_result = verify_read_piece(path, &f, vfi->piece_size, \
  251. vfi->piece_data, &vfi->piece_data_size);
  252. if (read_piece_result == 1) {
  253. /* End of file, try the next one */
  254. break;
  255. } else if (read_piece_result == -1) {
  256. /* Something failed */
  257. result = -1;
  258. fprintf(stderr, "Reading piece: %d failed\n", vfi->piece_index);
  259. break;
  260. }
  261. /* Else, buffer got filled, read target piece hash and continue */
  262. if (metainfo_piece_index(vfi->metai, vfi->piece_index, &vfi->expected_piece_hash) == -1) {
  263. fprintf(stderr, "Piece meta hash reading failed at %d\n", vfi->piece_index);
  264. break;
  265. }
  266. /* BUT don't increment piece_index here, because it will be copied into a thread */
  267. }
  268. /* Wait until a thread signals us to fill it's buffer, and check result */
  269. sem_wait(&mt_sem_needs_fill);
  270. pthread_mutex_lock(&mt_mut_tofill);
  271. if (mt_td_tofill->piece_data_size == vfi->piece_size && \
  272. !mt_td_tofill->results_match) {
  273. /* If there was a hash at least once and vertif failed */
  274. fprintf(stderr, "Error at piece: %d\n", \
  275. mt_td_tofill->piece_index);
  276. pthread_mutex_unlock(&mt_mut_tofill);
  277. result = -1;
  278. goto end;
  279. }
  280. mt_td_tofill->piece_index = vfi->piece_index;
  281. mt_td_tofill->piece_data_size = vfi->piece_data_size;
  282. mt_td_tofill->expected_result = vfi->expected_piece_hash;
  283. vfi->piece_index++;
  284. /* Reset variable so we will read next piece */
  285. vfi->piece_data_size = 0;
  286. /* Swap buffers */
  287. uint8_t* tmp = vfi->piece_data;
  288. vfi->piece_data = mt_td_tofill->piece_data;
  289. mt_td_tofill->piece_data = tmp;
  290. /* Send thread to work */
  291. sem_post(&mt_td_tofill->sem_filled_buffer);
  292. mt_td_tofill = NULL;
  293. /* Tell threads its okay to fill the tofill pointer */
  294. pthread_cond_signal(&mt_cond_tofill);
  295. pthread_mutex_unlock(&mt_mut_tofill);
  296. }
  297. end:
  298. if (f)
  299. fclose(f);
  300. if (read_piece_result == -1) {
  301. return -1;
  302. }
  303. return result;
  304. }
  305. #else
  306. static int verify_files_cb(const char* path, void* data) {
  307. verify_files_data_t* vfi = (verify_files_data_t*)data;
  308. FILE* f = NULL;
  309. int ver_res;
  310. if (!opt_silent) {
  311. vfi->file_index++;
  312. printf("[%d/%d] Verifying file: %s\n", vfi->file_index, vfi->file_count, path);
  313. }
  314. while ((ver_res = verify_read_piece(path, &f, vfi->piece_size, \
  315. vfi->piece_data, &vfi->piece_data_size)) == 0) {
  316. if (vfi->piece_size != vfi->piece_data_size) {
  317. fprintf(stderr, "piece_size != piece_data_size at hash\n");
  318. }
  319. sha1sum_t curr_piece_sum;
  320. verify_piece_hash(vfi->piece_data, vfi->piece_data_size, curr_piece_sum);
  321. const sha1sum_t* target_piece_sum;
  322. if (metainfo_piece_index(vfi->metai, vfi->piece_index, &target_piece_sum) == -1)
  323. goto error;
  324. if (memcmp(curr_piece_sum, target_piece_sum, sizeof(sha1sum_t)) != 0)
  325. goto error;
  326. vfi->piece_index++;
  327. vfi->piece_data_size = 0;
  328. }
  329. if (ver_res == -1)
  330. return -1;
  331. return 0;
  332. error:
  333. fprintf(stderr, "Error at piece: %d\n", vfi->piece_index);
  334. if (f)
  335. fclose(f);
  336. return -1;
  337. }
  338. #endif
  339. /*
  340. * Returns 0 if all files match
  341. */
  342. static int verify_files(metainfo_t* m, const char* data_dir, \
  343. int append_torrent_folder) {
  344. int result = 0;
  345. #if MT
  346. mt_max_thread = get_nprocs_conf();
  347. pthread_mutex_init(&mt_mut_tofill, 0);
  348. sem_init(&mt_sem_needs_fill, 0, 0);
  349. pthread_cond_init(&mt_cond_tofill, 0);
  350. mt_threads = calloc(mt_max_thread, sizeof(verify_thread_t));
  351. for (int i = 0; i < mt_max_thread; i++) {
  352. mt_threads[i].thread_data.piece_data = malloc(metainfo_piece_size(m));
  353. sem_init(&mt_threads[i].thread_data.sem_filled_buffer, 0, 0);
  354. if (pthread_create(&mt_threads[i].thread, NULL, verify_piece_hash_mt, &mt_threads[i].thread_data) != 0) {
  355. perror("Thread creation failed: ");
  356. exit(EXIT_FAILURE);
  357. }
  358. }
  359. #endif
  360. verify_files_data_t data;
  361. data.piece_size = metainfo_piece_size(m);
  362. data.piece_data = malloc(data.piece_size);
  363. data.piece_data_size = 0;
  364. data.piece_index = 0;
  365. data.metai = m;
  366. if (!opt_silent) {
  367. data.file_count = metainfo_file_count(m);
  368. data.file_index = 0;
  369. }
  370. int vres = verify_fullpath_iter(m, data_dir, append_torrent_folder, \
  371. verify_files_cb, &data);
  372. if (vres != 0) {
  373. result = vres;
  374. goto end;
  375. }
  376. /* Here, we still have one piece left */
  377. sha1sum_t curr_piece_sum;
  378. SHA1_CTX ctx;
  379. SHA1Init(&ctx);
  380. SHA1Update(&ctx, data.piece_data, data.piece_data_size);
  381. SHA1Final(curr_piece_sum, &ctx);
  382. const sha1sum_t* target_piece_sum;
  383. if (metainfo_piece_index(m, data.piece_index, &target_piece_sum) == -1) {
  384. result = -1;
  385. goto end;
  386. }
  387. if (memcmp(curr_piece_sum, target_piece_sum, sizeof(sha1sum_t)) != 0) {
  388. result = -1;
  389. goto end;
  390. }
  391. end:
  392. #ifdef MT
  393. for (int i = 0; i < mt_max_thread; i++) {
  394. pthread_cancel(mt_threads[i].thread);
  395. pthread_join(mt_threads[i].thread, NULL);
  396. sem_destroy(&mt_threads[i].thread_data.sem_filled_buffer);
  397. free(mt_threads[i].thread_data.piece_data);
  398. }
  399. free(mt_threads);
  400. pthread_cond_destroy(&mt_cond_tofill);
  401. pthread_mutex_destroy(&mt_mut_tofill);
  402. sem_destroy(&mt_sem_needs_fill);
  403. #endif
  404. free(data.piece_data);
  405. return result;
  406. }
  407. int verify(metainfo_t* metai, const char* data_dir, int append_folder) {
  408. int files_no_exists = verify_is_files_exists(metai, data_dir, append_folder);
  409. if (files_no_exists)
  410. return files_no_exists;
  411. return verify_files(metai, data_dir, append_folder);
  412. }