1 /*
2 This file is part of mktorrent
3 Copyright (C) 2007, 2009 Emil Renner Berthing
4
5 mktorrent is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 mktorrent is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19 #ifndef ALLINONE
20 #include <stdlib.h> /* exit(), malloc() */
21 #include <sys/types.h> /* off_t */
22 #include <errno.h> /* errno */
23 #include <string.h> /* strerror() */
24 #include <stdio.h> /* printf() etc. */
25 #include <fcntl.h> /* open() */
26 #include <unistd.h> /* access(), read(), close() */
27 #ifdef USE_OPENSSL
28 #include <openssl/sha.h> /* SHA1() */
29 #else
30 #include <inttypes.h>
31 #include "sha1.h"
32 #endif
33 #include <pthread.h> /* pthread functions and data structures */
34
35 #include "mktorrent.h"
36
37 #define EXPORT
38 #endif /* ALLINONE */
39
40
41 #ifndef PROGRESS_PERIOD
42 #define PROGRESS_PERIOD 200000
43 #endif
44
45 #ifndef O_BINARY
46 #define O_BINARY 0
47 #endif
48
49 #if defined _LARGEFILE_SOURCE && defined O_LARGEFILE
50 #define OPENFLAGS (O_RDONLY | O_BINARY | O_LARGEFILE)
51 #else
52 #define OPENFLAGS (O_RDONLY | O_BINARY)
53 #endif
54
55 struct piece_s;
56 typedef struct piece_s piece_t;
57 struct piece_s {
58 piece_t *next;
59 unsigned char *dest;
60 unsigned long len;
61 unsigned char data[1];
62 };
63
64 struct queue_s;
65 typedef struct queue_s queue_t;
66 struct queue_s {
67 piece_t *free;
68 piece_t *full;
69 unsigned int buffers_max;
70 unsigned int buffers;
71 pthread_mutex_t mutex_free;
72 pthread_mutex_t mutex_full;
73 pthread_cond_t cond_empty;
74 pthread_cond_t cond_full;
75 unsigned int done;
76 unsigned int pieces;
77 unsigned int pieces_hashed;
78 };
79
get_free(queue_t * q,size_t piece_length)80 static piece_t *get_free(queue_t *q, size_t piece_length)
81 {
82 piece_t *r;
83
84 pthread_mutex_lock(&q->mutex_free);
85 if (q->free) {
86 r = q->free;
87 q->free = r->next;
88 } else if (q->buffers < q->buffers_max) {
89 r = malloc(sizeof(piece_t) - 1 + piece_length);
90 if (r == NULL) {
91 fprintf(stderr, "Out of memory.\n");
92 exit(EXIT_FAILURE);
93 }
94
95 q->buffers++;
96 } else {
97 while (q->free == NULL) {
98 pthread_cond_wait(&q->cond_full, &q->mutex_free);
99 }
100
101 r = q->free;
102 q->free = r->next;
103 }
104 pthread_mutex_unlock(&q->mutex_free);
105
106 return r;
107 }
108
get_full(queue_t * q)109 static piece_t *get_full(queue_t *q)
110 {
111 piece_t *r;
112
113 pthread_mutex_lock(&q->mutex_full);
114 again:
115 if (q->full) {
116 r = q->full;
117 q->full = r->next;
118 } else if (q->done) {
119 r = NULL;
120 } else {
121 pthread_cond_wait(&q->cond_empty, &q->mutex_full);
122 goto again;
123 }
124 pthread_mutex_unlock(&q->mutex_full);
125
126 return r;
127 }
128
put_free(queue_t * q,piece_t * p,unsigned int hashed)129 static void put_free(queue_t *q, piece_t *p, unsigned int hashed)
130 {
131 pthread_mutex_lock(&q->mutex_free);
132 p->next = q->free;
133 q->free = p;
134 q->pieces_hashed += hashed;
135 pthread_mutex_unlock(&q->mutex_free);
136 pthread_cond_signal(&q->cond_full);
137 }
138
put_full(queue_t * q,piece_t * p)139 static void put_full(queue_t *q, piece_t *p)
140 {
141 pthread_mutex_lock(&q->mutex_full);
142 p->next = q->full;
143 q->full = p;
144 pthread_mutex_unlock(&q->mutex_full);
145 pthread_cond_signal(&q->cond_empty);
146 }
147
set_done(queue_t * q)148 static void set_done(queue_t *q)
149 {
150 pthread_mutex_lock(&q->mutex_full);
151 q->done = 1;
152 pthread_mutex_unlock(&q->mutex_full);
153 pthread_cond_broadcast(&q->cond_empty);
154 }
155
free_buffers(queue_t * q)156 static void free_buffers(queue_t *q)
157 {
158 piece_t *first = q->free;
159
160 while (first) {
161 piece_t *p = first;
162 first = p->next;
163 free(p);
164 }
165
166 q->free = NULL;
167 }
168
169 /*
170 * print the progress in a thread of its own
171 */
print_progress(void * data)172 static void *print_progress(void *data)
173 {
174 queue_t *q = data;
175 int err;
176
177 err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
178 if (err) {
179 fprintf(stderr, "Error setting thread cancel type: %s\n",
180 strerror(err));
181 exit(EXIT_FAILURE);
182 }
183
184 while (1) {
185 /* print progress and flush the buffer immediately */
186 printf("\rHashed %u of %u pieces.", q->pieces_hashed, q->pieces);
187 fflush(stdout);
188 /* now sleep for PROGRESS_PERIOD microseconds */
189 usleep(PROGRESS_PERIOD);
190 }
191
192 return NULL;
193 }
194
worker(void * data)195 static void *worker(void *data)
196 {
197 queue_t *q = data;
198 piece_t *p;
199 SHA_CTX c;
200
201 while ((p = get_full(q))) {
202 SHA1_Init(&c);
203 SHA1_Update(&c, p->data, p->len);
204 SHA1_Final(p->dest, &c);
205 put_free(q, p, 1);
206 }
207
208 return NULL;
209 }
210
read_files(metafile_t * m,queue_t * q,unsigned char * pos)211 static void read_files(metafile_t *m, queue_t *q, unsigned char *pos)
212 {
213 int fd; /* file descriptor */
214 flist_t *f; /* pointer to a place in the file list */
215 ssize_t r = 0; /* number of bytes read from file(s)
216 into the read buffer */
217 #ifndef NO_HASH_CHECK
218 off_t counter = 0; /* number of bytes hashed
219 should match size when done */
220 #endif
221 piece_t *p = get_free(q, m->piece_length);
222
223 /* go through all the files in the file list */
224 for (f = m->file_list; f; f = f->next) {
225
226 /* open the current file for reading */
227 if ((fd = open(f->path, OPENFLAGS)) == -1) {
228 fprintf(stderr, "Error opening '%s' for reading: %s\n",
229 f->path, strerror(errno));
230 exit(EXIT_FAILURE);
231 }
232
233 while (1) {
234 ssize_t d = read(fd, p->data + r, m->piece_length - r);
235
236 if (d < 0) {
237 fprintf(stderr, "Error reading from '%s': %s\n",
238 f->path, strerror(errno));
239 exit(EXIT_FAILURE);
240 }
241
242 if (d == 0) /* end of file */
243 break;
244
245 r += d;
246
247 if (r == m->piece_length) {
248 p->dest = pos;
249 p->len = m->piece_length;
250 put_full(q, p);
251 pos += SHA_DIGEST_LENGTH;
252 #ifndef NO_HASH_CHECK
253 counter += r;
254 #endif
255 r = 0;
256 p = get_free(q, m->piece_length);
257 }
258 }
259
260 /* now close the file */
261 if (close(fd)) {
262 fprintf(stderr, "Error closing '%s': %s\n",
263 f->path, strerror(errno));
264 exit(EXIT_FAILURE);
265 }
266 }
267
268 /* finally append the hash of the last irregular piece to the hash string */
269 if (r) {
270 p->dest = pos;
271 p->len = r;
272 put_full(q, p);
273 } else
274 put_free(q, p, 0);
275
276 #ifndef NO_HASH_CHECK
277 counter += r;
278 if (counter != m->size) {
279 fprintf(stderr, "Counted %" PRIoff " bytes, "
280 "but hashed %" PRIoff " bytes. "
281 "Something is wrong...\n", m->size, counter);
282 exit(EXIT_FAILURE);
283 }
284 #endif
285 }
286
make_hash(metafile_t * m)287 EXPORT unsigned char *make_hash(metafile_t *m)
288 {
289 queue_t q = {
290 NULL, NULL, 0, 0,
291 PTHREAD_MUTEX_INITIALIZER,
292 PTHREAD_MUTEX_INITIALIZER,
293 PTHREAD_COND_INITIALIZER,
294 PTHREAD_COND_INITIALIZER,
295 0, 0, 0
296 };
297 pthread_t print_progress_thread; /* progress printer thread */
298 pthread_t *workers;
299 unsigned char *hash_string; /* the hash string */
300 unsigned int i;
301 int err;
302
303 workers = malloc(m->threads * sizeof(pthread_t));
304 hash_string = malloc(m->pieces * SHA_DIGEST_LENGTH);
305 if (workers == NULL || hash_string == NULL) {
306 fprintf(stderr, "Out of memory.\n");
307 exit(EXIT_FAILURE);
308 }
309
310 q.pieces = m->pieces;
311 q.buffers_max = 3*m->threads;
312
313 /* create worker threads */
314 for (i = 0; i < m->threads; i++) {
315 err = pthread_create(&workers[i], NULL, worker, &q);
316 if (err) {
317 fprintf(stderr, "Error creating thread: %s\n",
318 strerror(err));
319 exit(EXIT_FAILURE);
320 }
321 }
322
323 /* now set off the progress printer */
324 err = pthread_create(&print_progress_thread, NULL, print_progress, &q);
325 if (err) {
326 fprintf(stderr, "Error creating thread: %s\n",
327 strerror(err));
328 exit(EXIT_FAILURE);
329 }
330
331 /* read files and feed pieces to the workers */
332 read_files(m, &q, hash_string);
333
334 /* we're done so stop printing our progress. */
335 err = pthread_cancel(print_progress_thread);
336 if (err) {
337 fprintf(stderr, "Error cancelling thread: %s\n",
338 strerror(err));
339 exit(EXIT_FAILURE);
340 }
341
342 /* inform workers we're done */
343 set_done(&q);
344
345 /* wait for workers to finish */
346 for (i = 0; i < m->threads; i++) {
347 err = pthread_join(workers[i], NULL);
348 if (err) {
349 fprintf(stderr, "Error joining thread: %s\n",
350 strerror(err));
351 exit(EXIT_FAILURE);
352 }
353 }
354
355 free(workers);
356
357 /* the progress printer should be done by now too */
358 err = pthread_join(print_progress_thread, NULL);
359 if (err) {
360 fprintf(stderr, "Error joining thread: %s\n",
361 strerror(err));
362 exit(EXIT_FAILURE);
363 }
364
365 /* destroy mutexes and condition variables */
366 pthread_mutex_destroy(&q.mutex_full);
367 pthread_mutex_destroy(&q.mutex_free);
368 pthread_cond_destroy(&q.cond_empty);
369 pthread_cond_destroy(&q.cond_full);
370
371 /* free buffers */
372 free_buffers(&q);
373
374 /* ok, let the user know we're done too */
375 printf("\rHashed %u of %u pieces.\n", q.pieces_hashed, q.pieces);
376
377 return hash_string;
378 }
379