2 This file is part of mktorrent
3 Copyright (C) 2007, 2009 Emil Renner Berthing
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.
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.
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
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() */
28 #include <openssl/sha.h> /* SHA1() */
33 #include <pthread.h> /* pthread functions and data structures */
35 #include "mktorrent.h"
41 #ifndef PROGRESS_PERIOD
42 #define PROGRESS_PERIOD 200000
50 typedef struct piece_s piece_t
;
55 unsigned char data
[1];
59 typedef struct queue_s queue_t
;
63 unsigned int buffers_max
;
65 pthread_mutex_t mutex_free
;
66 pthread_mutex_t mutex_full
;
67 pthread_cond_t cond_empty
;
68 pthread_cond_t cond_full
;
71 unsigned int pieces_hashed
;
74 static piece_t
*get_free(queue_t
*q
, size_t piece_length
)
78 pthread_mutex_lock(&q
->mutex_free
);
82 } else if (q
->buffers
< q
->buffers_max
) {
83 r
= malloc(sizeof(piece_t
) - 1 + piece_length
);
85 fprintf(stderr
, "Out of memory.\n");
91 while (q
->free
== NULL
) {
92 pthread_cond_wait(&q
->cond_full
, &q
->mutex_free
);
98 pthread_mutex_unlock(&q
->mutex_free
);
103 static piece_t
*get_full(queue_t
*q
)
107 pthread_mutex_lock(&q
->mutex_full
);
112 } else if (q
->done
) {
115 pthread_cond_wait(&q
->cond_empty
, &q
->mutex_full
);
118 pthread_mutex_unlock(&q
->mutex_full
);
123 static void put_free(queue_t
*q
, piece_t
*p
, unsigned int hashed
)
125 pthread_mutex_lock(&q
->mutex_free
);
128 q
->pieces_hashed
+= hashed
;
129 pthread_mutex_unlock(&q
->mutex_free
);
130 pthread_cond_signal(&q
->cond_full
);
133 static void put_full(queue_t
*q
, piece_t
*p
)
135 pthread_mutex_lock(&q
->mutex_full
);
138 pthread_mutex_unlock(&q
->mutex_full
);
139 pthread_cond_signal(&q
->cond_empty
);
142 static void set_done(queue_t
*q
)
144 pthread_mutex_lock(&q
->mutex_full
);
146 pthread_mutex_unlock(&q
->mutex_full
);
147 pthread_cond_broadcast(&q
->cond_empty
);
150 static void free_buffers(queue_t
*q
)
152 piece_t
*first
= q
->free
;
164 * print the progress in a thread of its own
166 static void *print_progress(void *data
)
171 err
= pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, NULL
);
173 fprintf(stderr
, "Error setting thread cancel type: %s\n",
179 /* print progress and flush the buffer immediately */
180 printf("\rHashed %u of %u pieces.", q
->pieces_hashed
, q
->pieces
);
182 /* now sleep for PROGRESS_PERIOD microseconds */
183 usleep(PROGRESS_PERIOD
);
189 static void *worker(void *data
)
195 while ((p
= get_full(q
))) {
197 SHA1_Update(&c
, p
->data
, p
->len
);
198 SHA1_Final(p
->dest
, &c
);
205 static void read_files(metafile_t
*m
, queue_t
*q
, unsigned char *pos
)
207 int fd
; /* file descriptor */
208 flist_t
*f
; /* pointer to a place in the file
210 ssize_t r
= 0; /* number of bytes read from
211 file(s) into the read buffer */
212 #ifndef NO_HASH_CHECK
213 off_t counter
= 0; /* number of bytes hashed
214 should match size when done */
216 piece_t
*p
= get_free(q
, m
->piece_length
);
218 /* go through all the files in the file list */
219 for (f
= m
->file_list
; f
; f
= f
->next
) {
221 /* open the current file for reading */
222 #if defined _LARGEFILE_SOURCE && defined O_LARGEFILE
223 if ((fd
= open(f
->path
, O_RDONLY
| O_BINARY
| O_LARGEFILE
)) == -1) {
225 if ((fd
= open(f
->path
, O_RDONLY
| O_BINARY
)) == -1) {
227 fprintf(stderr
, "Error opening '%s' for reading: %s\n",
228 f
->path
, strerror(errno
));
233 ssize_t d
= read(fd
, p
->data
+ r
, m
->piece_length
- r
);
236 fprintf(stderr
, "Error reading from '%s': %s\n",
237 f
->path
, strerror(errno
));
241 if (d
== 0) /* end of file */
246 if (r
== m
->piece_length
) {
248 p
->len
= m
->piece_length
;
250 pos
+= SHA_DIGEST_LENGTH
;
251 #ifndef NO_HASH_CHECK
255 p
= get_free(q
, m
->piece_length
);
259 /* now close the file */
261 fprintf(stderr
, "Error closing '%s': %s\n",
262 f
->path
, strerror(errno
));
267 /* finally append the hash of the last irregular piece to the hash string */
275 #ifndef NO_HASH_CHECK
277 if (counter
!= m
->size
) {
278 fprintf(stderr
, "Counted %" PRIoff
" bytes, "
279 "but hashed %" PRIoff
" bytes. "
280 "Something is wrong...\n", m
->size
, counter
);
286 EXPORT
unsigned char *make_hash(metafile_t
*m
)
290 PTHREAD_MUTEX_INITIALIZER
,
291 PTHREAD_MUTEX_INITIALIZER
,
292 PTHREAD_COND_INITIALIZER
,
293 PTHREAD_COND_INITIALIZER
,
296 pthread_t print_progress_thread
; /* progress printer thread */
298 unsigned char *hash_string
; /* the hash string */
302 workers
= malloc(m
->threads
* sizeof(pthread_t
));
303 hash_string
= malloc(m
->pieces
* SHA_DIGEST_LENGTH
);
304 if (workers
== NULL
|| hash_string
== NULL
)
307 q
.pieces
= m
->pieces
;
308 q
.buffers_max
= 3*m
->threads
;
310 /* create worker threads */
311 for (i
= 0; i
< m
->threads
; i
++) {
312 err
= pthread_create(&workers
[i
], NULL
, worker
, &q
);
314 fprintf(stderr
, "Error creating thread: %s\n",
320 /* now set off the progress printer */
321 err
= pthread_create(&print_progress_thread
, NULL
, print_progress
, &q
);
323 fprintf(stderr
, "Error creating thread: %s\n",
328 /* read files and feed pieces to the workers */
329 read_files(m
, &q
, hash_string
);
331 /* we're done so stop printing our progress. */
332 err
= pthread_cancel(print_progress_thread
);
334 fprintf(stderr
, "Error cancelling thread: %s\n",
339 /* inform workers we're done */
342 /* wait for workers to finish */
343 for (i
= 0; i
< m
->threads
; i
++) {
344 err
= pthread_join(workers
[i
], NULL
);
346 fprintf(stderr
, "Error joining thread: %s\n",
354 /* the progress printer should be done by now too */
355 err
= pthread_join(print_progress_thread
, NULL
);
357 fprintf(stderr
, "Error joining thread: %s\n",
362 /* destroy mutexes and condition variables */
363 pthread_mutex_destroy(&q
.mutex_full
);
364 pthread_mutex_destroy(&q
.mutex_free
);
365 pthread_cond_destroy(&q
.cond_empty
);
366 pthread_cond_destroy(&q
.cond_full
);
371 /* ok, let the user know we're done too */
372 printf("\rHashed %u of %u pieces.\n", q
.pieces_hashed
, q
.pieces
);